parameter可以定义一个参数(默认是32位)。在写代码时,对于某些数字,设计者经常利用定义参数的方式进行编写,方便修改,也方便阅读。
, A" h2 W3 a5 t& Q
在硬件电路中,使用计数器当做计时器,每记录一个数字等于过去一个时钟周期。由于本设计中采用的clk为50MHz,所以经过50_000_000(在verilog中,如果是描述数字,中间的下划线只起到分隔的作用,不影响数值的大小)个周期正好为1秒钟。由于计数器是从0开始计数,所以计数器只需要记录到50_000_000-1即可。
+ \% I# D5 W5 N& L! H
为了能够记录到50_000_000-1这么大的数字,所以定义了一个26位的计数器cnt(参考附录1:设计中位宽的概念和计算位宽的小技巧)。
) Y3 D7 P1 q3 s# _
在verilog中,“{}”( 大括号)的第一个特殊作用为位拼接。{a,b}相当于将a和b拼接为一个整体,并且是高位为a,低位为b。
1 b( c& A' ~! \3 S. ^( E
当led输出为4’b0001时,第一个led点亮;经过1秒钟,输出4’b0010时,第二个led点亮;经过1秒钟,输出4’b0100时,第三个led点亮;经过1秒钟,输出4’b1000时,第四个led点亮;经过1秒钟,输出4’b0001时,第一个led点亮······按照上述的过程周而复始,就形成了流水灯。
$ k2 d% _+ {3 l8 `7 d% V
不难发现,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]}。
: V" D/ J7 W, E: w; ^
- 仿真代码& q: [( @* m3 i& O" B
# u' b A4 R) c1 R
0 t2 {0 A/ X& H) t) i
7 p5 p- j0 ]4 ]1 F/ Z8 @+ Z2 t" w7 s. z" j7 x0 P/ o
0 k8 a a; m$ X9 [& ^" P2 f- A' G5 r/ B- l ^
/ T$ C2 X6 t& M2 j
$stop是一个系统任务,功能为将Isim的仿真停止。
7 j5 z( {) ] O2 Y
运行分析综合后,打开RTL仿真。
) r- `/ o6 X" L6 N# x4 l: y' d: T
- 波形分析0 W$ ~( r5 V' q6 W6 |( U
5 V/ C: p @, Q0 U& [4 y
, |# W% {& \. x4 r& C
' C3 L2 H2 Q/ Z; ?3 U! C在Isim中的Instances and Processes中,点开ledrun_tb,可以看到ledrun_inst,选中之后再Objects窗口中就会看到代码中的所有变量。我们选中cnt右键,选择Add to wave window。
9 ~% e8 `5 I; o+ S1 e* T
( [( z. }9 g$ L: R9 S% Q. J9 Q) @+ `
" C8 [3 Z) @0 V+ u( [& N
9 L$ ]* r4 C& n/ y1 D9 e9 w5 @ s, u' d+ ~8 j5 S* G
返回到wave窗口中,cnt信号已经添加到wave窗口中。由于新添加进来,没有数据(no data)。
. o) m" n. e/ m' T
* Q& e" {: O1 r* ~/ y
' |1 X# Y) {" t" N3 D3 V$ [, }. |
; M5 {- M3 Z+ \. v
点击restart。
9 E) Z+ f% \1 H$ l# W8 F. U# }* M8 |' H
0 Y( Z' Q d. m1 d6 w& V8 b* N
9 T7 [, f* K" @& p0 v0 Y J& ^
4 e, ?" `7 ~+ ?& b J, N- z2 ?5 M
0 `: I3 e& J0 h# m! K% krestart按钮为重新运行波形,点击后,波形全部消失,wave窗口中所有的波形都处于no data 状态。点击run –all按钮,开始运行波形。
* J7 v, S1 {: I" D/ Z% Z' k" X
' |2 e4 n% Z8 o# h
' E6 y* F: y7 i
" N, I2 J& r7 K! E* g
运行后,会自动停止。停止在tb文件中的$stop处。
& f( |- B0 a( ^$ }. I
返回wave窗口,各个信号都会有波形。
- I' r' O+ a! o& C, k
3 `5 m9 e# M3 [4 q, F
设置cnt的信号进制为无符号的十进制:右击cnt信号,选择radix中的unsigned Decimal。
" x1 f, M( `# v! M, g, M/ z
$ k( p6 G* @+ J, ~) G! Q
6 N: ]6 i6 L' ` m9 ^/ X( ]7 @
2 u; c6 z2 E: X$ [7 p把光标放到复位结束时,选择放大波形。
, q$ w0 l$ p7 `* J: _9 V% A% r5 w( M9 `
放大按钮的右侧第二个按钮为全局缩放,功能为将所有运行波形,显示到目前的窗口里;左侧第二个为缩小。最右边的按钮是显示到光标位置。
- f8 ~$ e2 @5 h# \
z8 T- Q4 g. y J% \" D
5 _# C% Q5 I5 J! y2 E0 i( y' y
9 D/ [& w6 d; q ?
可以看到,在复位结束后,cnt信号每一个时钟周期都会增加1。
. r p! G$ n5 S+ {* w/ V0 o
由于我们设计的流水灯是每1秒钟流动一个,在上述的仿真中,led数值是不会变化的。如果仿真几秒钟的话,仿真的时间会比较长。在此不建议仿真几秒钟的时长,有可能会导致电脑卡住。
- w0 ?( s7 y0 R
仿真时,可以将T_1s的值,改成一个较小值。例如:5。然后在此编译仿真。
' F& j8 Y/ m* L1 P4 P+ Z3 a
' r. F( R6 O4 [" c Q, d+ U; q; L
在ISE的编译器中,修改完后。进行综合分析,保证没有任何语法错误。点击Re-launch。
$ a3 R& p0 m( i# u* q0 }
5 ^7 d4 T: L% y3 ~. T) h, x0 [& v6 h6 W! D
[color=rgba(0, 0, 0, 0.9)]能够清楚的看到,led在进行移位,并且都是5个周期移动一次。
1 @. t3 d0 k% s' u! F( d* w# q; P5 s7 p, l. \
+ A! Y" \5 k ?3 v& V' ]
3 G9 ]+ j2 j b' o; `+ ^8 U/ E" u; T
" d2 l+ d# v5 s9 o) H0 }# D6 G+ ^4 C0 K1 O) d0 t/ e: V
2 K/ o. B ~: c0 V: H0 Z* o
: _8 Q* O" ^& t; u; K8 }% L
仿真通过后,关闭ISIM。回到ISE中,将参数修改成为50_000_000,综合分析后,分配管脚。布局布线,生成配置文件,进行下板测试。
; f# Q% l( J1 x7 R1 L开发板上的四个LED开始做流水状点亮。
/ o6 l: g8 V- g# g$ h; N