parameter可以定义一个参数(默认是32位)。在写代码时,对于某些数字,设计者经常利用定义参数的方式进行编写,方便修改,也方便阅读。
8 p, G; i# \8 _6 ^# V p
在硬件电路中,使用计数器当做计时器,每记录一个数字等于过去一个时钟周期。由于本设计中采用的clk为50MHz,所以经过50_000_000(在verilog中,如果是描述数字,中间的下划线只起到分隔的作用,不影响数值的大小)个周期正好为1秒钟。由于计数器是从0开始计数,所以计数器只需要记录到50_000_000-1即可。
' j, L0 e5 |7 @- m$ {: M
为了能够记录到50_000_000-1这么大的数字,所以定义了一个26位的计数器cnt(参考附录1:设计中位宽的概念和计算位宽的小技巧)。
: S0 }9 `8 D/ b8 ?; N5 v: r/ t
在verilog中,“{}”( 大括号)的第一个特殊作用为位拼接。{a,b}相当于将a和b拼接为一个整体,并且是高位为a,低位为b。
- y) A( ~) l8 J( x1 E8 R7 A2 _
当led输出为4’b0001时,第一个led点亮;经过1秒钟,输出4’b0010时,第二个led点亮;经过1秒钟,输出4’b0100时,第三个led点亮;经过1秒钟,输出4’b1000时,第四个led点亮;经过1秒钟,输出4’b0001时,第一个led点亮······按照上述的过程周而复始,就形成了流水灯。
2 g0 r M; E! t5 j( d, ~6 C; p
不难发现,led的输出,一直为3个0,1个1。并且1的位置每1秒钟移动一次,从头到尾,然后又到头。这种现象可以利用移位的思想进行实现。即:led[3]<=led[2]; led[2]<=led[1]; led[1]<=led[0]; led[0]<=led[3];如果将被赋值的组成一个整体,那就是led,赋值的组成一个整体就是{led[2:0], led[3]}。
* X: e6 X1 s- {0 N5 Q- r5 W d
- 仿真代码
4 g4 h. |; D. S6 A
2 d) l7 B4 s( H4 d ^
2 B, A' F2 K0 f6 A% u' W
$ M9 F- L: x' I' F* l7 V
) X2 E& j. ]& ~
7 B# L) J& I# r+ ~% E* X# O# k2 V! h, \
8 n3 u/ Q) g$ m$stop是一个系统任务,功能为将Isim的仿真停止。
. K; Q" C7 Q/ y! }/ t
运行分析综合后,打开RTL仿真。
- P1 r0 ]% m! ^ R
- 波形分析) F5 t/ `8 y, @+ n
0 U( k: \0 p; T" @' y/ [! Q' }
0 K# C* ^( V' c$ f0 j$ D% y2 i$ y
; O X1 d3 P/ C% V# y在Isim中的Instances and Processes中,点开ledrun_tb,可以看到ledrun_inst,选中之后再Objects窗口中就会看到代码中的所有变量。我们选中cnt右键,选择Add to wave window。
) n2 A* B4 |3 O2 U5 C# t& {3 n
- o; V0 b i( Z8 }% U: h b9 G
]/ O1 B- n+ i- \, o: l
+ Y( u& z# y) G' H9 z6 w* e7 h& G
" I N: m' R3 E7 R4 Q2 P6 P
返回到wave窗口中,cnt信号已经添加到wave窗口中。由于新添加进来,没有数据(no data)。
* F7 q: P* P# p7 B* v) @
( x4 Z6 E; ^* L0 K( S7 s
0 d4 N, ?) J3 E* l" A/ u
: N9 l8 r, O4 l7 z1 k7 Y$ A点击restart。
, b9 t6 i# O% |: X% O1 u) J
! [* l' v: i) P: s' y$ c
- F' K2 l5 S! H3 F: g: R' N0 y( Q% A9 U0 x9 h$ k3 k# y8 G
7 p! w/ s X3 q* _6 _, E
restart按钮为重新运行波形,点击后,波形全部消失,wave窗口中所有的波形都处于no data 状态。点击run –all按钮,开始运行波形。
! Y' P- W" @& C% n( \8 M, c
' P( f/ f' C% W+ O3 u# U
. z. A9 j U" h# Q1 @
s+ g5 T4 q( ?) Y1 A+ h7 ^
运行后,会自动停止。停止在tb文件中的$stop处。
4 ^1 P5 n! f3 ?. ]7 R
返回wave窗口,各个信号都会有波形。
4 y# a* g# q, U+ @) N$ t8 B
2 v" n6 ]# n; B, d! h设置cnt的信号进制为无符号的十进制:右击cnt信号,选择radix中的unsigned Decimal。
0 M, E# D# \0 x9 d
% A$ v$ ^# U- G
& h# s& m: X5 _' A4 M% L# P( ~
* s8 s* ~4 _$ e2 E! x7 O% b把光标放到复位结束时,选择放大波形。
" B: _8 u$ O2 A: z! x3 K1 V' l6 X1 `8 w6 N6 J
放大按钮的右侧第二个按钮为全局缩放,功能为将所有运行波形,显示到目前的窗口里;左侧第二个为缩小。最右边的按钮是显示到光标位置。
: o( P" i) B) z% c
6 L5 y5 q. ~, k6 t3 t/ u
J2 p3 g8 x/ z" Y7 G# l+ S) A
1 _! C3 w$ M8 [0 D8 l% v# m6 H: {! d
可以看到,在复位结束后,cnt信号每一个时钟周期都会增加1。
) K& [5 t9 _1 C' X+ H K4 {* K
由于我们设计的流水灯是每1秒钟流动一个,在上述的仿真中,led数值是不会变化的。如果仿真几秒钟的话,仿真的时间会比较长。在此不建议仿真几秒钟的时长,有可能会导致电脑卡住。
6 t* e7 _ o' ^/ ?
仿真时,可以将T_1s的值,改成一个较小值。例如:5。然后在此编译仿真。
9 t) [# S H! ^
, y1 j5 O! `8 J' A; ~; M `
在ISE的编译器中,修改完后。进行综合分析,保证没有任何语法错误。点击Re-launch。
' b* }9 h) Y, O, r: P
$ P" q+ L& x3 c
& L0 I E+ I% H2 Y% p: k+ u[color=rgba(0, 0, 0, 0.9)]能够清楚的看到,led在进行移位,并且都是5个周期移动一次。
+ i1 I4 P" M+ _$ W- w8 A. Y
$ j1 V" N. F% w6 J+ v' q6 `
. S9 _ P6 N, W# y9 L. j8 J
/ _ m. ?9 ], x/ G+ w3 J
! D% S: B0 N4 }( N+ H C& V. v+ U( _9 s5 T9 S9 Z+ j, f: N" `+ k1 y
0 @. P6 e5 E; g+ J* Y
4 O+ k0 \- h; d( g仿真通过后,关闭ISIM。回到ISE中,将参数修改成为50_000_000,综合分析后,分配管脚。布局布线,生成配置文件,进行下板测试。
5 u! y* v" U7 I+ i开发板上的四个LED开始做流水状点亮。
) a+ W3 }8 j2 k, W