一乐电子

一乐电子百科

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

搜索
查看: 5906|回复: 14
收起左侧

Verilog HDL 建模技巧 :低级建模 仿顺序操作 · 思路篇

[复制链接]
发表于 2010-6-5 21:48 | 显示全部楼层 |阅读模式
好东西尽收其中 这个是转以下这个仁兄的  akuei2's Blog+ |: x9 b+ e9 K9 x! H1 H* m
[Verilog HDL 建模技巧 :低级建模 仿顺序操作 · 思路篇] 1 - 封面+书语3 l, D0 P. X- t2 Y) k# ?
/ b; o$ q+ y! H0 X+ d

& O* [& D6 S( _) U. }, n
% R: P7 C, D$ s9 w. q8 c  d书语:

“低级建模”是针对入门或者新手提出的一种“设计思路”。我们知道FPGA执行的概念是“并行”,但是在入门众多的实验中,高达4成的实验都是“顺序操作”的执行概念。假设一个比较经典的例子“流水灯”实验。如果利用单片机设计流水灯的驱动程序,是非常简单,我们只要,建立延迟函数,移位函数,和一个大循环,就可以实验流水灯效果。

4 _$ F/ f1 l* V

但是换做另一个环境如(CPLD/FPGA)的平台上,实验复杂的流水灯效果。在“并行”概念上,设计这样一个驱动程式,会使得初学者上力不接下力。我们可以考虑这样一个问题:“用什么办法,不失CPLD/FPGA"并行性",而且还可以轻松实现"顺序操作" ?

在网上个多的论坛,时常可以看见很多新手求救关于这方面的问题,笔者也是如此,所以笔者才决定写这本笔记。

/ p; |. j: v4 g  u6 S" P; L$ q; L

还有一种问题,就是编程风格的问题。Verilog HDL语言对初学者来说,编程风格是最一个大难题的。有这样冷笑话流传在新手之间流传:“ 如果一个新手写十个驱动程式,会超过10个不同的编程风格 ”。这也难怪的,要建立一个编程风格真的不简单,即使是写了多年经验,也不见得会有编程风格。然而“低级建模”有固定的模板格式,可以很好的帮助新手们。

* i8 x6 w5 h5 V8 e' D) q" ?

除此之外,“低级建模”对“仿顺序操作”是简洁的,不同于一般的编程,滥用了“状态机”来达到效果。“状态机”在仿顺序操作上,虽然有很大的效果,但是“状态机”对资源的消耗,和“代码的臃肿”都是可见的。“低级建模”在“仿顺序操作”上,使用“步骤”的概念,就如我们食饭的时候,首先饭来张口,然后慢慢的咽,最后才吨进食道,就这样一个动作就结束。要吃另一口饭的时候,再重复这个动作。(如果吃饭换做是状态机,事物可能会永远卡在食道)


- r+ N- q( o+ }, x6 u1 b2 c% q$ g

目前笔记也只是写了“低级建模”的“思路篇”而已,因为笔者考虑到自身资格和经验的问题,笔记的续文非常不适宜。但是笔者认为这已经是足够了,因为笔记的内容可以很好的帮到初学者,从另一个角度去认识Verilog HDL建模技巧。


3 D3 }" J1 l  _$ ~2 V- L' e

笔者的话:

0 M! P. V: d# B

还记得自己初次接触Verilog HDL语言时候的感觉吗?曾经有没有因经历失败而灰心呢?如此过来的人,偶尔会回忆,会觉得入门(初学)时的心情是最真实的。即使现在,挫折中,迷失中,混沌中。只要回忆当时,就有继续的勇气 ......

 楼主| 发表于 2010-6-5 21:49 | 显示全部楼层
前言:

再过不久就要毕业了,毕业后就要上班,所以呀学习的时间确实比以前少了许多。不过这不是重点,我发觉自己的“自学”程度已经达到某种的限制,再也提升不了多少,是时候换换环境。故一在此之前写下一本关于Verilog HDL 心得的学习笔记。


* R. h% N3 ~: |: P# P

每一本笔记的开始都有一个初衷:在早期练习Verilog HDL 语言(以下简称V语言),有一种莫名的奇怪感觉。相信很多初学者曾经有过这样的感觉 ......

我对这感觉执着很久,似乎要揭开什么!?我以简单的模块,进行了许多样的V语言编程风格,最后我发现有一套很简单而且非常“有效”的方法,我称为“低级建模”。


( ~1 ]* ]' a( P3 }

一开始,我对这与这方法没有任何“准则”要遵守而非常茫然,要我消耗了很长时间才建立起最基本的“准则”。经过许多的实验,惊讶的发现该方法对于“仿顺序操作”非常有效,我开始猜想“是不是应该建立一个适合低级建模的模板呢?”。模板的概念,有学过C++的朋友应该都知道,在C++的世界里,模板可以根据不同的“类型”,以同样的格式,创建函数,类等等。

, n" S9 w7 u, g

在最后的几个试验中,我结论出一种“通用”的编程模板。即使,各个模块都有不同的功能,不同的代码量,不同的编程习惯,但是固定的“形状”还是存在。你应该知道V语言的解读性,不是一般的“穷”!(除非你经验老道)

# @* S8 t& `0 d, K, L( h9 X

可能会出现“鄙视群众”,“批评”自不量力的我搞原创的活儿。这一点我承认,自己才学过几个月的V语言和FPGA而已,没有任何实际的项目经验,V语言都无法精通,根本没有任何资格...  


; c3 s3 T6 G, R. n* Q; H

被这样认为是人之常情,所以我才要事先声明:


& p9 H0 P/ k+ u1 ^0 J4 J) y  u5 L7 x

这只是个人的一个心得,一个思想而已,纯粹“分享热心”才写这一本笔记。笔记的内容好不好我不知道,有没有用我也不知道,但是有一点,对于初学者来说,它绝对有参考的价值。仅此而已。


) m/ U/ Q' d; O

这本笔记我不论任何评价。至于该笔记的价值如何,竟可以在读前先不要做任何结论,浏览过后或许你会萌出其他的思路呢?

 楼主| 发表于 2010-6-5 21:50 | 显示全部楼层
1“低级建模”的思路8 z  g2 o1 V3 M: L

首先,我将用一个简单的例子来说明一下,“低级建模”的最基本思路:

(一)利用C语言驱动八位发光二极管:

, s  x% W6 @0 k! S2 a* j; A! {; ]

http://space.ednchina.com/upload/2010/6/4/8d556145-fa4a-4fb8-a8bf-874d086f1a52.jpg            

(如果我说C语言和单片机是形影不离的哈,估计没有人会反对吧?


# H2 ~! e7 S; ]

我们以流水灯作为例子,因为它是最经典的实验。假设我要实现流水灯效果,那么我只要建立一个简单的“流水灯函数”函数,“Flashing”。


& \' G9 @7 Y& M% Q# N

void Flashing(){ ...... }  


" ], i, w- Z5 d9 z4 L

http://space.ednchina.com/upload/2010/6/4/27ce8e29-6ad5-4d5b-83ae-6b6fd1f39b7b.jpg

! z* u4 U% [7 }

如果要实现自左向右或者自右向左发光的流水灯,可以使用C语言创建两个简单的函数,Flashing_To_Right”和“Flashing_To_Left”。

. E2 V. G! y* j3 W


/ e3 p; f  L4 [# e! |0 B

   void Flashing_To_Right() { ...... }

void Flashing_To_Left() { ...... }


" ?5 d, x  Q  h


$ a- U) a" Z/ T- ]

假设我要实现流水灯效果:

1. 永远自右向左发亮。

2. 永远自左向右发亮。

3. 流水灯永远跑来跑去。

- U% f  W( r, x

那么可以这样写:

6 A$ w: z" ]' F, {; _( n, T  p

    while( 1 ) { Flashing_To_Left(); } //1

    while( 1 ) { Flashing_To_Left(); } //2

while( 1 ) { Flashing_To_Left(); Flashing_To_Right(); } //3

对于C语言来说,这些任务都非常的简单。几乎是入门级的实验,但是将这些实验带入到V语言的环境下。确实一个记得思考的问题。


6 a8 [1 S$ K9 Z5 R

(二)利用V语言驱动八位发光二极管:

. }- r8 X  Z6 F+ Z8 u

我们先看一段非常傻瓜的一段代码:


+ H' K+ Z2 V/ B! T# ?/ g  f8 n5 D, e/ W

; S: b6 s. Y+ [0 x

    module Flashing ( CLK, RSTn, Data_Out );

! Y* U7 @+ ]* Q1 X' p& E

    input CLK, RSTn;

    output [7:0]Data_Out;


* K7 P/ s1 y  ~' s2 ?0 N8 p1 d. S* m

reg [7:0]Counter;


( }  _) q" A0 X! e1 O- F

always @ ( posedge CLK or negedge RSTn )

    if( !RSTn )

        Counter <= 8'd0;

    else if( Counter == 200 )

        Counter <= 8'd0;

    else

        Counter <= Counter + 1'b1;

8 B, F+ [$ s! r" _5 m; j* k

reg [7:0]i;

4 I  x7 B/ \2 l/ l5 N

always @ ( posedge CLK or negedge RSTn )

     if( !RSTn )

        i <= 8'd0;

     else if( Counter == 200 )

        i <= i + 1'b1;

     else if( i == 8 )

        i <= 8'd0


1 l  Q* }, ?( c: [8 P* _% T" `

reg [7:0]rData;

7 S  s) u9 P$ m6 g

always @ ( posedge CLK or negedge RSTn )

    if( !RSTn )

        rData <= 8'b0000_0001;

    else if( i < 8 )

        rData <= { rData[6:0], 1'b0 };

    else if( i == 8 )

        rData <= 8'b0000_0001;

9 C% T7 a# U! G! I# {0 G- f9 R- n( A

assign Data_Out = rData;


2 t3 s; Y1 K9 r) W2 _

endmodule


; S4 M9 I$ s! w) w


5 N0 Q* p1 r/ j% ~

没错,上面是实现流水灯的代码。如果我说我要求:“ 自左向右循环3次,自右向左循环5次,然后自左向右一次,自右向左一次,然后自左向右循环30 ”。当你听到这样的要求,你可能会崩溃.... 如果按照上面的写法,你会写得很长很长。

; f! X% L/ C+ K1 v- N

相比之下,C语言要实现以上的要求,根本就是“小儿科”的功夫。

, K/ v# `7 w, Y4 J3 W6 B7 ~

9 J- G3 f; G# W  ~0 J2 X

    int i;

    for( i = 0; i < 3; i++ ) Flashing_To_Right();

    for( i = 0; i < 3; i++ ) Flashing_To_Left();

    Flashing_To_Right();

    Flashing_To_Left();

for( i = 0; i < 30; i++ ) Flashing_To_Right();


  q7 {  ]# b$ P8 {& j% U0 ]0 z- I

$ I. ^2 @% ?6 ^2 H( j

给自己5分钟的思考,想想我到底要表达什么?

2 a9 Z, G- |7 i" ?

C语言上,有“顺序操作”或者“泛型编程”的概念。从上述的代码中,for循环利用i变量,控制循环次数,然后调用3次“Flashing_To_Right()”函数。相反的V语言是“并行操作”的概念,类似的方法完全行不通。这就是新手们常常遇到的问题。


& @2 ]+ w* c3 h9 B  y! u! [* v

方法行不通,但是不代表思路不行。“低级建模”最基本的思路就是“仿顺序操作”。“低级建模”不是什么困难的东西,它只是一中的“手段”而已,只要你了解它的基本构思,它会成为很有用的工具。

 楼主| 发表于 2010-6-5 21:51 | 显示全部楼层
2章“低级建模”的结构2.1低级建模”的基本结构

从一个管理系统看来,“低级建模”会是一个从上直下层次的一个概念。

http://space.ednchina.com/upload/2010/6/4/e760f799-20f2-42e9-bff5-b29e3f32ec75.jpg

从上面的示意图可以看出,老板是最顶级的,而员工是最低级的。老板从上头发号,然后经经理呀,领导呀,最后苦力集团就开始动工了。当完工的时候,一一向上报告。低级建模的概念类似如此。除了老板以外,所有经理,领导,员工的“集合”称为“低级建模”。而老板是“独立模块”,因为老板不受命令,而且也不用报告。而多个“功能模块”的集合称为“组织模块”,如上面示意图中的“永远垂死的苦力集团”

2.2“低级建模”的准则
: {! F/ h# Z5 C" `

根据上文和上示意图的分析,“低级建模”基本上有以下几个准则:

1. 有“组织模块”和“功能模块”之分。

2. “低级建模”中的“功能模块”均称为“低级功能模块”。

3. “低级功能模块”有如特点:有开始信号,完成信号,一个模块只有一个功

    能。

4. 多个“低级功能模块”组织起来称为“组织模块”。

5 i) l8 E6 I3 v+ b

注意点:功能模块又分成“低级功能模块”和“非低级功能模块”?这话何解呢?

6 q! o; O. S, l, H


: E0 p+ W/ w, m" b( K) @/ K

http://space.ednchina.com/upload/2010/6/4/705e0fbf-e98a-43a1-86d5-4893e5037de9.jpg

从上面的示意图中可以分辨出“低级功能模块”和“非低级功能模块”。“低级功能模块”包含“开始信号”和“完成信号”而“非低级功能模块”则没有(就是这样简单而已)。

 楼主| 发表于 2010-6-5 21:52 | 显示全部楼层
2.3“开始信号”和“完成信号”的作用

" N% ^/ d4 b) Y- N+ M

先看看以下一段代码:

# Q  m. O! D: y2 I  I$ E$ Z

   

   //独立模块8 r$ V) S6 y" G. X  V- V
    alway @ ( posedge CLK or negedge RSTn )
) T8 ]  B2 j" E8 H3 g3 `9 Q     ......
* S; O7 _; E; d% l9 s: b7 h     else/ M* @3 H% [4 Q5 |- u' k: P
          case ( cState )6 Y! J2 s8 g8 ^9 r: N2 y
* F5 \! l2 H$ N7 m
* j5 x; r$ k, T$ H" F) J
              "打扫" : ( O/ \" G8 W& R9 a, L
               if( 打扫完成 ) nState <= "洗厕所"4 g3 y( A1 E' j- [! g. G
              else "发号打扫命令"
* |' U( R5 [6 s! l& s5 h* M) H7 H. p
5 h+ @' P7 l* n
               "洗厕所" :
! G4 N4 c* |0 K; Y              if( 洗厕所完成 ) nState <= "跑腿"; q' u. J  z! C0 |8 d& W" |8 e* u
              else "发送洗厕所命令"
+ |) Y( B6 G5 y2 q* o4 n2 R  c
8 E% `9 ^; O8 Q1 B) c- `0 S$ t# F8 K5 L1 B  J9 J6 P% j
              "跑腿" :
! c/ C: T& x' ?& |9 F! Q, M) ~2 v; s; ]; @              ....../ [( @6 V. t7 O& T, e
* f/ a9 A  I% ~" d9 ^0 ^. I6 b( I
6 r# Z0 i1 U: C$ `7 C
/*********************************************************************/           ' Z8 ~# [+ n1 A# _3 N. t
% {8 _8 D2 k* Q. \9 i3 L

) J0 E1 V0 v1 k  @. F    //低级功能模块1 - 打扫工作
" t/ s9 O% B4 j  O4 n always @ ( posedge CLK or negedge RSTn )9 W6 T" M9 N& t
     ......- `8 y( |, ^4 I, G. Z" e
     else" A  W/ V2 T/ V8 f- H+ d3 l
         case( cState )
; E: A' u+ @' I1 O2 C+ c. k# z" n: F
5 ?4 G" I7 {) \$ h! J3 ?
               IDLE :
/ }8 t( X* h! _; `" s, F9 Y               if( 到扫命令 )  nSate <= "打扫";
- N- N) U5 S/ k  K8 w              else nState <= IDLE;
  y( [, W7 q! i
- O0 Z3 Z1 Z1 G( R
1 y7 k7 ]" m: j  U+ g2 l              "打扫" :$ p# ^# C+ B5 B; F
              "执行打扫" ;  p; s8 q* Y: [( V( c4 G" ~  e7 R
              "打扫完毕后报告" ;0 p* m; b1 B" R) x
              "待命..." nState <= IDLE;* _1 P" P: o' M+ C2 F3 d5 r. C
                  ......& [2 }% \1 d/ H- ?8 C( M

* O' N8 e5 J% l0 ~/ c* c' p3 o: ~9 `; q
/*********************************************************************/
$ c1 K+ |! G0 ]" O* _0 S: N8 f) O5 B2 \

: m3 i$ Q1 w3 D( h5 {. X    //低级功能模块2 - 洗厕所工作7 n  {- c: j7 E4 d$ [: P& X+ k9 D
always @ ( posedge CLK or negedge RSTn )
% u- c& g# J; ]* W  g     ......
- I) x, k1 N6 D, U     else if
) O. H, [- ~) N$ b0 t/ n         case( cState )
/ c1 X) n- G/ C: V$ H' h  C
2 ?  i& G1 k5 |5 c; `' V& k
8 j5 R; F" Z3 _               IDLE:
' C7 f2 V, K8 n6 u5 T" y' b+ E. i              if( 洗厕所命令 )  nSate <= "洗厕所";
$ l7 r2 u) Z8 `# ], Y2 L              else nState <= IDLE;- A( G7 U, e+ ^$ i. b
) q- m: h/ p0 W9 z
7 k8 p! c( w! W
               "洗厕所" :
7 U: i+ I8 n  D, v( \              "执行洗厕所" ;: G3 u1 d) w2 J5 B
              "洗厕所完毕后报告" ;
+ D/ J0 r; k2 L* t; v              "待命..." nState <= IDLE;
$ P  d$ L( p  u, k' t. V              ......
0 @" h$ q5 E) d3 `0 D, C7 q1 n1 y; s! l! ?, P9 C3 N
2 c$ U1 V" W$ T, o; G' i0 k
/*********************************************************************/: F& y7 N6 {; [5 D8 B  b- z2 \. w
2 W3 P; \, U: N; {# I4 I! _
8 o4 R$ |, B5 Q: y% A, q0 h* {
//低级功能模块3 - 跑腿工作& U) v8 E. F0 `! m$ B, v6 K) |  r. i
always @ ( posedge CLK or negedge RSTn )
+ S* ^/ }- u* g" c     ......
6 w" y* w% f/ }" ~     else
* Y! F5 w! r& N, F: t         case( cState )6 p5 p2 H7 [# i7 k+ k

* o' L  N! {% [- ^
1 U1 C% k+ c/ M9 h4 Y2 ^               IDLE:" g) L3 _6 Z* N  J2 c1 [3 R# b3 E
              if( 跑腿命令 ) nState <= "跑腿";           
( A9 u. W" ]4 ]& S4 W: @               else nState <= IDLE;* ?0 X6 P) E7 y% K8 I
4 Q) I4 c* m9 Z2 E4 r
% P/ R$ Y  K! G6 P: q7 |: l$ u
              "跑腿" :; I7 s1 Q$ q7 j
                 ......


, b* X& e) L% B5 ?, r+ \

上面的代码可以分成两个部分,一部分是“独立模块”和另一部分是“低级功能模块”。独立模块只有一个则打工模块有三个,而且每一个打工模块仅包含一个功能而已“打扫”,“洗厕所”和“跑腿”....

注意: 独立模块不属于低级建模。

http://space.ednchina.com/upload/2010/6/5/8d7a1a60-20d3-4b06-ade3-c291dfc92b5d.jpg

假设老板有一系列的命令要发号:打扫 ==> 洗厕所 ==> 跑腿

当“负责打扫”的“低级功能模块”收到老板的第一号命令“打扫”时,该模块从“待命状态”变成“打扫状态”,此时老板可以睡一觉或者干其他的活儿。该模块便开始“执行打扫任务”,当该模块“打扫完毕”后,就给老板“报告”,然后返回“待命状态”。故老板听到“打扫完成”报告后,就给下一个“低功能模块”发下一号命令 ...

在“低级建模”的结构上,为了使不同层次的“低级功能模块”可以协调的工作,“开始信号”和“完成信号”扮演着很重要的角色。在现实中,如果 “打扫 ==> 洗厕所 ==> 跑腿”是一个有“次序的三部曲”,那么老板不可能要员工颠倒次序来干活儿 。老板得按次序,一个一个的命令员工干活。除此之外老板也不可能实时监督员工的工作状况,做老板真的很辛苦,除了“发号”以外,还要干很多事情,所以员工的“完成报告”在某种程度上可以减轻老板的活儿(使编程更简单),毕竟老板也是人,他也有疲惫的时候。

( p7 m. r& m" S7 E* C; ]

接下来的话题便是:“每一个低功能模块仅包含一个功能”。

3 I, c7 [9 W3 @+ k1 t9 d) g

虽然在现实中,确实存在“全能的人类”打扫,洗厕所,跑腿等技能全都集于一身。但是“低级建模”的准则必须遵守。你尝试想象一下,如果一个“低级功能模块”,包含了如上的工作 “打扫 ==> 洗厕所 ==> 跑腿” 或者更多,即不是要把代码写得很长很长 ...

所以呀,“低级建模”的准则有它一定的“重要性”(在日后的深入中,你会慢慢了解的)。

+ A4 c& Z: m7 i: B1 W# M. J4 k( ~1 U

 楼主| 发表于 2010-6-5 21:53 | 显示全部楼层
2.4 组织的概念
8 p9 x" A1 Q/ K. \

“组织模块”在“低级建模”中,非常的重要。它不但简化对多个“低级功能模块”的调用,而且也解决了“两义性”或者“多义性”的问题。


1 Y1 ?' }% w- {% W8 M

你尝试想象一下:如果有多个打工仔,散落在不同的地方。当老板要发号的时候,既不是非常不方便。同样的,在模块化设计中,设计者往往为了使使用更简单,常常都会使用“顶层模块”将多个模块“封装”到一个模块中,亦即将复杂的东西“隐藏”了起来,只剩下简单“接口”而已。这样的做法是为了使该模块可以容易被使用。

$ J4 S$ k5 D' s' w$ ^4 }7 A7 g

http://j.imagehost.org/0312/PIC1e.jpg


4 J9 ~; f! i4 `

然而在“低级建模”的设计中,“模块化的组织”更有“层次感”。为了使“上一层模块”可以很方便调用“下一层组织模块”。“低级建模”的设计常常将一组或者一个“组织模块+低级功能模块”,“低级功能模块+低级功能模块”,“组织模块+组织模块”组织起来。虽然感觉上会有一种“杂乱感”,但是实际运用起来,真的非常方便。


4 p6 c! j# r) I( v1 A

如上面的示意图中,3个员工被组织了起来,然后3个员工的组合又和领导组织了起来。故这样的组织方法因层次关系,如此类推,最后会有两个“大组织”

9 ^+ J! w* X2 g2 R( Q& ^) h! W7 B

组织1 = { 经理 => 领导 => 3个用工 }

组织2 = { 经理 => 3个员工 }

“低级建模”的“组织”结果会是示意图中所示。除老板意外,大家都有自己的“组织”。


0 E9 A) o% i2 G6 T9 |5 t, f" o

假设老板要命了员工干活,那么老板只要命令任意一个经理就行。

至于“二义性”或者“多义性”的问题,后面会讨论到。

 楼主| 发表于 2010-6-5 21:55 | 显示全部楼层
3.1 模板基本结构


; M# ]7 k9 r  q) F' t: Nmodule Template

4 \( Y( \8 E: V6 ?. w; |4 d
(

* Z6 u& u: k9 z- J1 X" r
    CLK, RSTn,


8 E7 s/ @8 r, e. u4 {# t5 j    ...... ,  // "n个输入输出"


4 i" ?' g$ d- i# Z" O5 `% l    Start_Sig , Done_Sig


4 P% N9 L3 i! f" K);

- z( ~/ V  g; Y  I  z8 |

       input CLK;

       input RSTn;

       input Start_Sig;

       input Done_Sig;

       ......  // "n个输出输入声明"

0 ?2 `8 B8 K8 ?

       /*******************************************/


6 k; u' ^1 y4 c/ I; t

      reg [3:0]i;

      reg isDone;

      always @ ( posedge CLK or negedge RSTn )

           if( !RSTn )

               begin

                   i <= 4'd0;  isDone <= 1'b0;

                   ......   // 任何复位的动作

               end

           else if( Start_Sig )

               case( i )
- O; r) b  }0 d* I/ x# B, Q' ?

                   4'b0 :  // 一般都是用于初始化

                   Init;

                   ......   // 任何相关的步骤

                   4'b n + 1, 4'b n + 2 :  //最后两个步骤用来产生完成信号

                   Done;       1 d& H4 J$ Q# b: k0 l  {

               endcase

         /*******************************************/

4 X- d- t5 ?$ ]! Q

         task Init;

3 l7 i% R& s1 G( Q+ [  X

             begin

                ......          //任何初始化的动作

                i <= 1 + 1b'1;  //指向下一个步骤

             end

         endtask

         /*******************************************/


3 q: z' I/ W) j! Z0 M8 Z

         task  "nTask";

             .......          // "nTask的执行任务"

         endtask

         /*******************************************/


: p6 e4 X- A; l# C) Z1 J

        task Done//产生Done信号

( X" Q: y1 A5 J. S

            if( isDone == 1 ) begin isDone <= 1'b0; i <= 4'd0; end

            else begin isDone <= 1'b1; i <= i + 1'b1; end; J% W# i. E2 R! u7 C0 U/ ]

        endtask

        /*******************************************/

  K, ~5 o/ |' E' v- k6 P6 c

        assign Done_Sig = isDone;

        ......  // 相关的输出

    endmodule

从上述中,模板的基本结构有以下的特征:


1 D& r4 D: ~6 N1 Z( O' Z

1) Start_Sig Done_Sig是固定的。

2) 寄存器i用于控制次序步骤。

3) 最后两个i步骤用于产生完成信号。

4) i 等于 0 的时候,多半都是用于初始化动作(选择性)。


3 |% ]; I8 o/ e& K& N) h

正如准则的要求,“开始信号”和“完成信号”都是必须的。“开始信号”可以视为“片选信号”而“完成信号”如字面上的意思。寄存器i有一个重要的功能,它指向任何下一个步骤,而通常所编写的格式如下:

- k. F6 L$ n' N, P- ~& A: P  w  q* V

: F# l! t. P4 p) k
i <= i + 1;

! |- ^% z1 m5 w2 ?' Z; c: J6 B) E

除此之外该模板还引入了 “task - endtask”。目的是为了提升和 “结构性” 。新手们应该知道,使用V语言如果没有良好的编程风格,代码的 “可读性” 是非常的糟糕。

1 Z* F: g) c+ J; t

在这里我先简单复习一下,“task - endtask” 的用法 :


, c  k) T8 J% u3 _% }

7 o0 Y0 N- s- t7 T7 U
  reg [3:0]i;  reg isDone;  reg [7:0]rData;  always @ ( posedge CLK or negedge RSTn)  if( !RSTn )      begin          i <= 4'd0          isDone <= 1'b0;          rData <= 8'd0;      end  else0 |6 Q6 r+ y* B) g4 d" Y% Y) F
      case ( i )! b' _% ?! a# O4 v
4'd0 : i <= i + 1;              ......      endcase

( a' R6 o$ c7 a+ I2 R) n8 n( h; x5 B5 {7 c, c* k0 m( R, ~" A
reg [3:0]i;  reg isDone;  reg [7:0]rData;  always @ ( posedge CLK or negedge RSTn )  if( !RSTn )      begin8 d4 a: ^0 f+ [. R0 k% y1 H
i <= 4'd0;          isDone <= 1'b0;          rData <= 8'd0;      end  else      case( i )
8 Q  K' \1 \* t) w$ T  @
3 [$ {& s$ Y& w4'd0 : Next;          ......; r! r& ]: u" d$ K! E
      endcase
# I# M9 q: C0 M% `9 v+ a9 |$ M' E /**********************************/( t; K' V' ~8 h
  task Next;
; K, _7 t5 w  K# I  l7 r . M8 f0 }9 S1 M
i <= i + 1'b1;, m; M. z& Y7 r" b
  endtask


- H2 b. o4 B; L" `$ W

上面的两个写法都是等价的。如果模块是小功能,那么左边的写法很适合。但是一旦模块的功能很复杂,那么右边的写法会凸显出优势。
( F4 Z1 O4 q7 N) o


$ p3 R0 G% `) f2 p3 ~7 `
 楼主| 发表于 2010-6-5 21:55 | 显示全部楼层
3.2 建议$ S( U  O! }4 A2 j0 F4 y

为什么需要模板结构?

0 E+ {' D2 l3 G# X- _" H  b

创建代码的工作往往都是一次性,为了供人参考,或者为日后“升级”的打算。我们不得不养成好的“编程风格”,这也是许多参考书上提出的重点之一。而“模板”便是一种已经制定好的“编程风格”,故这样会简化了编程风格上的问题,只要加以修改,便会完整一个有“结构”和“有风格”的代码。

$ a& F( Q2 `" `" a: x

为什么“低级功能模块”的步骤,需要一个计数寄存器来指向呢?


: P( s# V6 V$ S: V" _; ~7 Z+ A

其实这个问题我也考虑了很久,因为是“仿顺序操作”的关系,故人类对“1, 2, 3 ... ”类似的次序(步骤)有更直接的效果。而且也很好的为代码扩展。

8 \  W" I% B3 _

编写“低级功能模块”时,必须遵守笔者提议的模板结构吗?

) a! G# n1 y3 b+ A+ S

模板的结构只是一个参考而已。该“模板”结论是我经过不同风格的编程,得出“最通用”的结果。当然你可以无视我的规定,完全自定义自己的模板结构。但是有一点请注意,必须以“解读性”为优先考虑。因为好的代码不是在“执行效率”上,而是“可维护”和“被解读”。

 楼主| 发表于 2010-6-5 21:56 | 显示全部楼层
4.1建立2个“低级功能模块”
+ r- M2 U2 ?3 I8 m1 a. F

“ 自左向右循环3次,自右向左循环5次,然后自左向右一次,自右向左一次,然后自左向右循环30

3 I& V4 O* h0 G: y" r8 g, s3 y- S

假设这是实验的要求, 首先我们先建立两个“低级功能模块”。一个名为flashing_to_left flashing_to_right


% `( Q8 z) T0 i( X

1.module flashing_to_right

2(

3.    CLK, RSTn,

4.    Start_Sig, Done_Sig,

5.    Q

6.);

7.

8.    input  CLK;

9.    input  RSTn;

10.    input  Start_Sig;

11.    output Done_Sig;

12.    output [7:0]Q;

13.
4 K7 O* @( f/ W& w

14.  O: J) [! H) I! R
/***********************************/

15.+ @0 J$ k) k! ]; j, t  m  p+ b

16.    parameter DELAY = 8'd200;

17.* m$ C. d: P. t" ?8 C3 `

18.$ v1 S# `( I+ B/ Y
/***********************************/

19.
8 E* r" q$ E2 k% e# ]% R( f' c5 f- `

20.    reg [7:0]Counter;

21.
0 J# N; h) U3 S1 k9 }* T& L

22.    always @ ( posedge CLK or negedge RSTn )

23.        if( !RSTn )

24.            Counter <= 8'd0;

25.        else if( Counter == DELAY )

26.            Counter <= 8'd0;

27.        else if( Start_Sig )

28.            Counter <= Counter + 1'b1;

29.        else

30.            Counter <= 8'd0;

31.9 J/ o, u9 t! b7 Y* B1 Z& I

32.  d0 l2 u2 y, A, f1 A3 q
/***************************************/

33.. P+ y. p; C: H) N+ C

34.    reg [3:0]i;

35.    reg [7:0]rData;

36.    reg isDone;

37.# [1 g7 P9 s$ l3 v7 u

38.    always @ ( posedge CLK or negedge RSTn )

39.        if( !RSTn )

40.            begin

41.                i <= 4'd0;

42.                isDone <= 1'b0;

43.                rData <= 8'dx;     

44.            end

45.        else if( Start_Sig )

46.            case( i )

47.& B! G0 l% a  b

48.
; W9 ]% A. l4 f  x6 @/ T* h% c1 M# a5 s9 Y! l: ]" J$ K$ _8 E
, i/ h- _, T! P3 V; Y7 z9 d

/ g" S" |& x5 K3 B: L% w7 ^4'd0 :

49.   E) F, `" ]- _* @1 U1 P7 e9 p
begin rData <= 8'dx; i <= i + 1'b1; end

50.
% ~3 r1 Q) [7 s4 t" V! D" I* ?) D. Y7 w8 H4 t
7 H" h: }6 Z! T# ^
8 m6 R2 u/ p# L

51.
+ l# ?' w  L' A: F7 M6 h5 q# l# d
: w9 B: Y+ u: Z( [) R1 d
: T3 F2 Q9 r4 b: F% Z) G0 R

7 ^) [; V! [# a, b+ j  i& R4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8 :

52.
2 c0 I2 |9 c3 @9 x( G
- E. t: z# W2 }
$ Y3 |9 V# p9 D

/ l: R& q% `$ R4 f! h' ?if( Counter == DELAY ) i <= i + 1'b1;

53.   d( V; E2 C: h8 p6 j* }& ?+ N
) c4 P5 L5 T( c
  n9 h' l: o* h* K& h" s) }
) H; n* {, t' z. \. R
else rData <= ( 8'h80 >> i - 1 );

54.' a0 f+ t2 E: A  u. ?; m

- h2 Y9 x% G3 t- t
" V4 [2 C* ]3 `% ?! a' O7 K: w. u* \* u/ X! m( E/ r

1 ~: B( p6 w% r: _( L3 s$ h8 d! R& C

55.
2 t. u3 \8 {2 d  s$ L8 `9 s8 c1 W4 l

: n2 X3 L5 T8 e) f. R. L7 g  v2 K+ J0 i/ N* A3 A9 U
* R1 o' B  |' |9 s" B4 _
4'd9 :

56.7 W+ k# }4 u1 m/ u
/ q) b7 S" V8 f7 Q: Q4 V9 M4 |1 P

5 |+ O- m' a' f
2 e8 ?1 m/ A: g3 X& K
; a1 D2 u' L' ?" f9 A" X  a1 Lbegin i <= 4'd10; isDone <= 1'b1; end

57.
) |  O  U$ r6 r' h1 {2 E6 G3 X4 V, t0 a  U! W, w% V

' Z( j; z0 o( i- m# W: ?
0 S- ^* F4 j- I& ~2 O) c* F$ R

58. ) m  G0 Q6 X: q

0 \1 G$ ^* r3 h' E" Z, W+ Z
$ }, B9 q2 [9 c
- n, Q1 b8 m0 k0 n8 E3 [4'd10 :

59.
, V8 Z+ e8 }0 j! H. d

# ?8 S! m0 p! Z0 G& y& z" u6 x% ^2 [# P# _% D

  G, e9 K+ |' ]9 u: Lbegin i <= 4'd0; isDone <= 1'b0; end

60.1 O+ V- F  t$ z" J. ]1 h

. B5 k* @' P# x# _6 `- F4 r( Y, b( n2 r" O7 N; A

( p8 g- j( J. X: _0 V

61.            endcase

62.

63.
7 e7 o. D% M. K+ I

64.) ?0 H" }% i& }9 z6 p. V% }
/**********************************/

65.

66.    assign Done_Sig = isDone;

67.    assign Q = ( i > 0 && i < 9 ) ? rData : 8'dx;

68.
! m& S! X9 q4 h! `, e" J( w6 h

69.' K9 `, f; C- m8 w% L& N8 a
/**********************************/

70.

71.endmodule


2 U1 o, F* ^/ T( K( y  _

' b2 R2 l2 Z& v

1.module flashing_to_left

2.(

3.    CLK, RSTn,

4.    Start_Sig, Done_Sig,

5.    Q

6.);

7.

8.     input  CLK;

9.     input  RSTn;

10.    input  Start_Sig;

11.    output Done_Sig;

12.    output [7:0]Q;

13.
. V, S" @3 K4 u1 k

14.2 p7 b% P' b! t/ ^& ]4 U6 P
/***********************************/

15.
8 y9 ^  c, C) J4 y- k9 [

16.    parameter DELAY = 8'd200;

17.
! A& A+ ~- u* N- I

18.
4 X) [6 ]5 ]1 z8 Q! ?. v& P5 C/***********************************/

19.( ~# y, r" D" i! m0 w

20.    reg [7:0]Counter;

21.7 w% F5 t7 e) n0 @

22.    always @ ( posedge CLK or negedge RSTn )

23.        if( !RSTn )

24.            Counter <= 8'd0;

25.        else if( Counter == DELAY )

26.            Counter <= 8'd0;

27.        else if( Start_Sig )

28.            Counter <= Counter + 1'b1;

29.        else

30.            Counter <= 8'd0;

31.# P! z. X1 s/ b8 ]& g

32.& r' a; T; ~% w3 q
/***************************************/

33.
% i- h5 U9 |( Z" D

34.    reg [3:0]i;

35.    reg [7:0]rData;

36.    reg isDone;

37.
9 n8 p1 l  h0 b0 p: K# p

38.    always @ ( posedge CLK or negedge RSTn )

39.        if( !RSTn )

40.            begin

41.                i <= 4'd0;

42.                isDone <= 1'b0;

43.                rData <= 8'dx;     

44.            end

45.        else if( Start_Sig )

46.            case( i )

47.% t" \. `9 c2 @9 Z; Q

48.
3 O5 M6 f/ J* ]) R

3 X# y2 G; m1 t7 @7 S7 y& T4 x" x

  L. X( O0 N7 C1 f. D( b$ P4'd0 :

49. - C; }! q( E; Y& G' F, [. {; h

. G0 L# J$ o4 w# }2 [+ n1 T. r2 I$ f0 G/ b) Y$ L3 M

- [8 I; s' R: @5 F, a, tbegin rData <= 8'dx; i <= i + 1'b1; end

50.7 n4 f( {8 m, Q. ]! _
2 [7 X3 c  h9 K% H

2 p1 {  |" L4 i& L0 I( A0 o( A# M5 P  A( }( }% s* A% O  G

51.
+ \% e3 J. H( `# g6 C
2 X* v, d1 z( I; A( B! s  V9 k
- C4 W( Y, o! G" [1 ]% L" J

6 E" _% i8 t) M  q* u! M# s: T" A4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8 :

52.5 c2 l( I5 F; N; m- F) B
$ \1 n2 N9 e7 e' n
' c2 H; w% N9 m/ s  C8 [& O
2 t; W/ t# P$ ~' @0 h: r/ m

0 }! ~1 x" O- _/ z& _' a# aif( Counter == DELAY ) i <= i + 1'b1;

53.
' u! @5 T1 L( [' z! Q9 [* e- i
4 T5 g' s) y, ?4 w8 u
2 E7 |: @7 a5 ~4 T
  ~7 |/ c0 \; E, R# W' u: y
else rData <= ( 8'h01 << i - 1 );

54.5 B& j' W/ W& p0 B' G/ R

# J, h. ?: L; I+ U5 H, V5 X; M  U1 w/ D' T4 F
8 n, M+ b; E, C2 |

' ~6 ?9 d. x) J; W) C4 R2 K5 j

55. ( M* f" B1 a( |! j7 u( L; H- m6 u
! \+ a  Q) E4 @% }
( Z0 _% A# \' h$ r2 o& {7 d
8 I+ ^0 N" ~& ]1 q  s0 w
4'd9 :

56. 6 `. h7 |$ }( K8 |6 S
4 a" d( r- G7 J9 {8 M7 H3 l: o5 V
: O6 q. n9 I+ Q1 Z

. s1 u7 `: a" u- {begin i <= 4'd10; isDone <= 1'b1; end

57.5 h5 g) j" N$ Z4 N0 s% Q, j
0 i! r, o& l2 w4 d' |

9 n! g4 v' G) P7 E' ~1 R8 y6 U
$ w5 ]& ^" g: {# S* t. K

58. 5 I; E) T) Q! j4 m; S' G
5 S# B; f" [  D  `" F& B( l

4 U1 _  Z1 n; x3 r# K) H0 ]1 D
1 V. A# _+ _! C4 i4'd10 :

59. , G4 R7 `. V& O4 ?5 d+ W0 y1 Y% a
0 _$ |7 c  j5 x0 A6 G6 [, C

, N6 j* ?' ]. ^  b9 i9 B' ^! C. L* F- J, i
begin i <= 4'd0; isDone <= 1'b0; end

60.4 w& W& _4 R$ F/ g% P6 w) K* H

6 t2 M' O1 r* n
- P* o2 C4 z/ L  k6 }
7 A# \) b' r# d$ c

61.            endcase

62.. l4 S- K# o; `9 I: a( y& D! e

63.
* S8 q( u1 u- w6 F /**********************************/

64.

65.    assign Done_Sig = isDone;

66.    assign Q = ( i > 0 && i < 9 ) ? rData : 8'dx;

67.  Q/ |1 ^0 w$ l  W5 K

68.
6 o; }3 W' ^0 k: \/**********************************/

69.

70.endmodule

两个模块几乎是一模一样,不同的地方只是在 53行而已。虽然代码都很简单,但是还是稍微关心 45 ~ 61 行的代码,因为是功能的核心部分。正如模板结构般,在第45行,如果Start_Sig 不处于高电平,那么该模块根本无法执行功能。计数寄存器i使用与指向下一个步骤,正如在第49行,当rData 被初始化过后,i指向下一个步骤。

- t  \. p+ W3 p! U  a( z

51行到53行之间,是移位操作,每一次的移位动作都需要200个时钟周期。移位操作一共有8个步骤。最后该模块产生一个“高脉冲”以表示“完成”,然而i也被复位为0


4 H( y7 U/ f' r: ?

仿真效果如下

http://space.ednchina.com/upload/2010/6/5/a27937ce-915e-4ed0-9e21-7a1e0b72070a.jpg

(第一行是输出Q,第二行是Start_Sig,第三行是完成信号)

& g6 m+ Y' _1 w


, Y. }$ S, x- ?1 I2 x: k

, @7 G: E9 l, l: @4 o. K

( 最后一行是完成信号 )

http://space.ednchina.com/upload/2010/6/5/dbc4e2e6-fa71-46aa-8cdd-9c8d94da7a39.jpg

2 j: V6 u, l; N2 G, ^

注意最后一行,当i 计数从1~8过后,就产生一个完成信号,而完成信号需要两个时钟周期的时间。


' y! S' T! H7 h3 E8 H完成创建2个“低级功能模块”以后,为了使日后调用方便,必须封装起来。“低级功能模块”的封装工作会比较麻烦,因为“低级功能模块”有“复用连线”的现象,换一句话说,会出现“两义性”或者“多义性”的问题 ......
 楼主| 发表于 2010-6-5 21:57 | 显示全部楼层
4.2 “低级功能模块”封装 和“两义性”或者“多义性”的问题

http://a.imagehost.org/0020/PIC4_3a.jpg

如上的示意图。当我们要封装2个“低级功能模块”在一次,如果两个“低级功能模块”使用同一个输入(重用输入连线)或者同一个输出(重用输出连线)就会出现两义性的问题。为了解决这个问题,使用“多路选择器”便可以。

  D& k$ {+ z1 S. z, [. U

1.module flashing_module

2.(

3.    CLK, RSTn,

4.    Right_Start_Sig, Left_Start_Sig, Right_Done_Sig, Left_Done_Sig,

5.    Q

6.);

7.
9 V' B, O6 G6 a3 f+ D: J

8.     input CLK;

9.     input RSTn;

10.    input Right_Start_Sig;

11.    input Left_Start_Sig;

12.8 [9 b" `% x3 X  Y5 q/ ?- E0 r

13.    output Right_Done_Sig;

14.    output Left_Done_Sig;

15.    output [7:0]Q;

16.
1 F3 ]9 x9 e' c( d  C0 p1 k' e

17.
' u, y9 a/ p, L4 M6 c( b /*******************************/

18.4 L# x; Y0 d9 S" X, Y1 h

19.    wire [7:0]Q_U1;

20.
4 n) |2 j5 H' R( ~% K1 y( [  L

21.    flashing_to_right U1

22.    (

23.        .CLK( CLK ),      
" v; F8 G1 N  ]+ w3 @
) t- ~$ P4 P2 g( S" L3 V
! T: E) ]4 Q3 A- `/ O// input - from top

24.        .RSTn( RSTn ),- w  q( o$ q8 e1 f* x6 C' J

# R8 R, s% \2 N
+ T; o7 i( a4 n3 F$ `% M, h& ~3 X+ W3 Z5 N" @1 p

0 g8 u1 l) I6 [" V& s// input - from top

25.        .Start_Sig( Right_Start_Sig ), & r6 e& q- i' F) Y
// input - from top

26.        .Done_Sig( Right_Done_Sig ),  // output - to top

27.        .Q( Q_U1 )
4 y. h9 F0 J9 N: l! d2 e1 c3 S6 X" I$ C6 T2 H5 q* J. P# i$ q
: H9 D- F. t' E' y1 w6 h0 R
  R* Z6 b* ?' w, r
# D7 r( _& M( O9 M

% p& H4 a$ c1 U! S' S8 ~3 H* l5 W// output - to wire

28.    );

29.
; _' M7 q9 N+ v5 a6 h

30.
- k- h# s- x& ]8 u/ U! e7 Y6 d% I) Q/********************************/

31.
) j7 g. R9 `& o( e

32.    wire [7:0]Q_U2;

33.+ N) l3 O0 l: W

34.    flashing_to_left U2

35.    (

36.        .CLK( CLK ),           
# ~' G5 |( f( {$ Y8 q" Q7 o$ @7 U
6 D- a& a2 U8 `// input - from top

37.        .RSTn( RSTn ),            3 L& `0 W' i9 @# `4 G3 [1 ^. ]
// input - from top

38.        .Start_Sig( Left_Start_Sig ),  
+ ]/ a, N1 Z' |! _( @' \1 h// input - from top

39.        .Done_Sig( Left_Done_Sig ),* b. D' N) W+ \: P- v9 j
// output - to top

40.        .Q( Q_U2 )4 n: O& s9 b$ w& f# ^# e$ _: j, d

" H6 o1 }. U( c5 `
3 F4 _  p6 E6 w+ r" Y# w, @: q; D& `6 {- l" s" g* \

- z) {+ t! K1 s& T, W1 k9 R
2 V+ f6 H9 m5 _. n3 B// output - to wire

41.    );

42.
. t' Y1 J0 |  U; I0 ]2 |

43.
2 m0 Q, H2 q* R7 X# X/*************************************/

44." j9 Q6 k3 j- D" J2 ~

45.    reg [7:0]rQ;

46.
/ W) x4 v& O& Q, p. U9 s

47.    always @ ( * )

48.    if( Right_Start_Sig ) rQ = Q_U1;

49.    else if( Left_Start_Sig ) rQ = Q_U2;

50.    else rQ = 3'dx;

51.( e: q) h( W/ G8 Y( i* t7 Y" C/ [

52.    assign Q = rQ;  

53.. N7 p! Z$ K# K

54., j0 f6 S6 B* x" q3 e, g6 A
/*************************************/

55.2 n* L! V9 r& S& Q9 u

56.endmodule    , y% v8 Y" b' \- O

0 }' ]' y) j! Y* s0 R+ m# P


4 e3 ?4 J" ^6 c0 {; @

为了解决多“两义性”或者“多义性”的问题多路选择器常常被使用。如上述代码中45~52行(从第27,第40行引出“连线”)Q_U1”和“Q_U2”被if控制着输出。这样的写法是最优化的,生成的RTL图也非常的整洁。

1 [3 f0 S  n( J9 G9 ~& i

http://space.ednchina.com/upload/2010/6/5/0f6b6eb3-8c6b-4af8-bcdc-6946a733b70a.jpg


$ s/ B2 g, K9 r$ R: j  l! K

当然还有其他的写法:


* d7 I' H  Z9 Y" q3 l

assign Q = Right_Start_Sig ? Q_U1 : Q_U2 ;

5 N0 B; U8 M3 A) o2 [# y1 d

虽然如上的写法和,第4552行的写法相比,更为简洁,而且生成的 RTL图也一样。但是这样的写法有如下的弱点:


) u6 m7 ?! r0 e4 o+ P. u  r4 [" e

1. 解读性很差。

2. 只能解决两义性的问题而已。


5 x$ o- n3 u! O% \9 J

所以不怎么推荐使用。


7 j& N6 U" U' C+ L0 ~

还有一中更糟糕的写法:


# q! z, F) X! o1 j  A

    reg [7:0]rQ;


0 U+ U* [1 n& ~& u. n

    always @ ( * )

    case( { Right_Start_Sig, Left_Start_Sig } )

        2'b10   : rQ = Q_U1;

        2'b01   : rQ = Q_U2;

        default : rQ = 3'bxxx

    endcase

# T2 b* r( k: G! ]

assign Q = rQ;

虽然该写法的解读性很高效果也一样,但是却很浪费资源。生成的RTL图如下:

6 d. Y% e" n  F% ~8 ~

http://space.ednchina.com/upload/2010/6/5/2f9ae0c3-d220-4ce9-b86b-ebf273901983.jpg


: Z: Q. Q. A- z  `

和上述两个写法相比,它可差多了,所以不推荐使用。

0 s+ h5 n5 U# z, W3 [( E) r1 B

/**********************************************************/


: S+ w5 D6 R8 ?5 W* u6 a7 s

    reg [7:0]rQ;


1 [4 R& T4 a8 W, C' Z

    always @ ( * )

    if( Right_Start_Sig ) rQ = Q_U1;

    else if( Left_Start_Sig ) rQ = Q_U2;

    else rQ = 3'dx;

+ m2 v) j; D$ R8 P! g6 {

assign Q = rQ;  

  L3 w* R/ a$ Z1 x


( X% [1 I! o" q8 `& A$ f$ ?

接下来,我们来分析上面的代码。

, p& g* [5 y' Y4 N1 v4 z


* |5 E) P$ L/ ~3 A7 g4 p

    always @ ( * )

0 f' M  G$ R- W+ H$ e$ Z' {3 @


( g, i! y" D0 P* o2 T) ~

always @ ( * )”这样的写法在Verilog HDL 2001 中已经被支持(好像是这个版本)。在敏感包中的“*”,可以理解为“任何状况都有效”。


5 X& U# S1 T6 K7 h" @; b


5 D6 S  ?5 p5 `% Q

    if( Right_Start_Sig ) rQ = Q_U1;

    else if( Left_Start_Sig ) rQ = Q_U2;

else rQ = 3'dx;  //不能省略掉

* r, \3 x4 H9 r: a6 i& g! W6 F


2 q- u! _& C1 e( m+ c' v

而“else rQ = 3'dx”,这行不能被省略掉。不要问我为什么,这是V语言的编程规则。你尝试注释掉后,再编译看看,你会发现会很多“Warning”。


+ ^8 u0 o  T3 A- \5 A/ I- D4 x0 j

- l2 W- _# o. w$ ]0 v; F% i1 F

       always @ ( * )

9 ~- Y2 }6 e: i1 S  [; b1 G  S
x   if( Right_Start_Sig ) Q = Q_U1;//连线 QQ_U1 Q_U2 之间没有


' b' u$ W& z" q, L* ]# t; I9 ?# Y7 zx   else if( Left_Start_Sig ) Q = Q_U2;' X4 q" \; Y/ b0 ^
//寄存器驱动


- z9 T0 A- |7 Bx   else Q = 3'dx;


. ~% E  a* }8 i2 l: x, i

还有一点请注意,因为“Q_U1”和“Q_U2”是连线的关系,所以必须使用寄存器来驱动。如上的代码中“rQ”便是扮演这样的角色。如果该寄存器被省略了,会出现编译错误。

* B$ i+ r# w) m0 t

总结:

“两义性”或者“多义性”的问题,不仅在“低级建模”中出现,日常的建模也会出现它们的踪影,然而“低级建模”出现的频率比较高罢了。“多路选择器”在设计的时候应该经多方面的考虑,亦即取得最平衡的效果。

3 y7 R  `+ v5 q! D

本版积分规则

QQ|一淘宝店|手机版|商店|电子DIY套件|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2024-5-19 00:44 , Processed in 0.079903 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表