parameter可以定义一个参数(默认是32位)。在写代码时,对于某些数字,设计者经常利用定义参数的方式进行编写,方便修改,也方便阅读。
$ W$ N9 N* b n( J h U; n
在硬件电路中,使用计数器当做计时器,每记录一个数字等于过去一个时钟周期。由于本设计中采用的clk为50MHz,所以经过50_000_000(在verilog中,如果是描述数字,中间的下划线只起到分隔的作用,不影响数值的大小)个周期正好为1秒钟。由于计数器是从0开始计数,所以计数器只需要记录到50_000_000-1即可。
; o0 c8 H2 j3 B [) L
为了能够记录到50_000_000-1这么大的数字,所以定义了一个26位的计数器cnt(参考附录1:设计中位宽的概念和计算位宽的小技巧)。
7 V' `( A8 ]9 j$ [) x2 @7 s) s" j
在verilog中,“{}”( 大括号)的第一个特殊作用为位拼接。{a,b}相当于将a和b拼接为一个整体,并且是高位为a,低位为b。
8 n4 v1 x. [- |+ q$ n+ S6 Y5 ?
当led输出为4’b0001时,第一个led点亮;经过1秒钟,输出4’b0010时,第二个led点亮;经过1秒钟,输出4’b0100时,第三个led点亮;经过1秒钟,输出4’b1000时,第四个led点亮;经过1秒钟,输出4’b0001时,第一个led点亮······按照上述的过程周而复始,就形成了流水灯。
3 H; `: q k ~5 h2 |! Y2 W
不难发现,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]}。
2 p$ C# q9 Y1 _6 h9 ?) r
- 仿真代码
! r( j3 Y% Q* D* f% P M* t( Q+ K$ O/ L: R8 e5 Z/ p) z X
# C4 t+ w9 ^/ G2 a! u- O
6 s6 l. B, s* x4 d4 f# ^0 _" s6 c( q( s; _% O3 w: S% B+ c
7 \; l# @ Y1 p5 J8 a; b3 i# ?+ k. U8 z7 i9 b0 D9 \! a2 ^
9 X4 }3 F/ A6 p4 Y) J g# @. g$stop是一个系统任务,功能为将Isim的仿真停止。
- C# l1 ~: x" T* I* x3 W8 G
运行分析综合后,打开RTL仿真。
2 |# a) {1 P& a" z& L
- 波形分析
& l# a i: X0 M) |8 W
# d) p' h- B2 d1 w
& G- y- x9 O/ ?" r- M0 ^! }& ?
在Isim中的Instances and Processes中,点开ledrun_tb,可以看到ledrun_inst,选中之后再Objects窗口中就会看到代码中的所有变量。我们选中cnt右键,选择Add to wave window。
6 v' n3 Y# L6 w
! ^( Q3 E/ v4 w2 y" f( ]9 {; F
0 C# u3 y; j* q
; [% N. H$ W7 @! m2 a8 K
' p! L+ U% [1 Q0 ^% b9 J/ s
返回到wave窗口中,cnt信号已经添加到wave窗口中。由于新添加进来,没有数据(no data)。
% b9 H; g% p& I$ Z% z. [& S
- B/ x$ c) \% O0 `" C" z, Y. _
% P$ f' Y& K9 i" e) T* G& a
9 ]# W$ t. e2 U" p点击restart。
" `9 H* |$ W! |4 J. }, G/ T
* j; J& f, z* {3 {
( h+ W' g, e; m
! _' W/ w& N0 x9 O, Z
5 S9 T N& B# |) l. grestart按钮为重新运行波形,点击后,波形全部消失,wave窗口中所有的波形都处于no data 状态。点击run –all按钮,开始运行波形。
4 x) M: Q# a/ L& s% L' t- p
5 d z, i4 U9 H* t$ C
# e: B! ^& {# e2 a- F
$ \) p4 C% [7 ]: H0 f+ Q$ b9 z/ f
运行后,会自动停止。停止在tb文件中的$stop处。
* q/ H! h" y9 o
返回wave窗口,各个信号都会有波形。
[/ [/ [* u* }4 q, W8 r' M1 I+ R
6 m: ?9 J0 Q0 |* X+ G \+ I
设置cnt的信号进制为无符号的十进制:右击cnt信号,选择radix中的unsigned Decimal。
: ]5 T1 ?! e) Z' e( k
( m1 i2 Y1 K; @1 @
5 ]9 D5 u" Z {
8 i. Q G/ c# a: R2 ]0 F把光标放到复位结束时,选择放大波形。
' g* w" K; O, G# I: N
9 g$ q2 T! B* Z% Q4 M- e% `3 y
放大按钮的右侧第二个按钮为全局缩放,功能为将所有运行波形,显示到目前的窗口里;左侧第二个为缩小。最右边的按钮是显示到光标位置。
+ c' C4 B% I1 x' o6 F
; |! V: G9 h1 |+ \/ j
1 D j) }" ~5 L2 X" D; B2 r2 v
' W ~$ e s4 {8 t! _2 w! Y
可以看到,在复位结束后,cnt信号每一个时钟周期都会增加1。
. y5 Y1 p) }6 D0 W; [- C, R
由于我们设计的流水灯是每1秒钟流动一个,在上述的仿真中,led数值是不会变化的。如果仿真几秒钟的话,仿真的时间会比较长。在此不建议仿真几秒钟的时长,有可能会导致电脑卡住。
9 D$ k7 T8 \' P6 X4 {# _
仿真时,可以将T_1s的值,改成一个较小值。例如:5。然后在此编译仿真。
" k% y( B0 G: E% }* ?+ }8 x- b
/ `$ |; B: i9 |
在ISE的编译器中,修改完后。进行综合分析,保证没有任何语法错误。点击Re-launch。
% G6 \9 [2 K+ `5 w9 D6 S! Q
! s! w I5 e) G7 Z
+ l; L$ H% v% b |4 y: C[color=rgba(0, 0, 0, 0.9)]能够清楚的看到,led在进行移位,并且都是5个周期移动一次。
% ~& K0 c1 t# I2 `1 J2 L
6 G/ P) Z" @0 S& H
' A' C/ d6 S, }4 `% W& T
( ]1 R/ F$ K6 c. y( d! V, f1 f" z' p1 O( O+ _0 L
$ U' X Y/ i8 W. k2 v7 R5 K0 b1 b
% ]! C+ p; q0 b
( F& I# {2 l1 j
仿真通过后,关闭ISIM。回到ISE中,将参数修改成为50_000_000,综合分析后,分配管脚。布局布线,生成配置文件,进行下板测试。
2 `" W* |3 ]$ r5 ]" d4 T
开发板上的四个LED开始做流水状点亮。
0 H- O2 s5 o/ N3 O8 K