在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 23182|回复: 31

[原创] Verilog语法精粹

[复制链接]
发表于 2012-3-18 15:10:10 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 johnny1209 于 2012-3-18 15:27 编辑

题记:



要记住,你们在学校里所学到的那些奇妙的东西,都是多少代人的工作成绩,都是由世界上每个国家里的热忱的努力和无尽的劳动所产生的。这一切都作为遗产交到你们手里,使你们可以领受它,尊重它,增进它,并且有朝一日又忠实地转交给你们地孩子们。这样我们这些总是要死的人,就在我们共同创造的不朽事物中得到了永生。
                                                                                                 ——爱因斯坦

前言:
      如果说C语言是思想的提炼的话,那Verilog则是思想+实体的提炼。一个复杂的数字电路,Verilog可以像“庖丁解牛”手上的那把钢刀,几下子就把这电路理清楚了。从C语言跳到Verilog,让我感受到Verilog化腐朽为神奇的思维之美。
      理解一门语言背后的思维,才能体会到语言真正的美。
      ——本文试图提炼一些Verilog语言里让人心动的语法设计。

一、nets&variable(之前为Registers)
      所有的数字电路中的物理连线、数据存储和传输单元不外乎4个逻辑值状态——0、1、X或x(不确定或未知的逻辑状态)、Z或z(高阻态)。故不像软件语言,数字电路的所有数据类型都是在上述4类逻辑状态中取值。
      故Verilog的变量只分为两种数据类型(不可综合的其他变量都未列出),为分清楚足够分清楚两种类型,在中文文献里没找到太好的解释,只好拿来英文了:

  • Nets
    - represent structural connections between components.
  • Registers
    - represent variables used to store data.




    也就是说,Nets表示数字电路中的连线,如门与门之间的连接,net型不存储数值(trireg型除外)。net型必须由驱动来驱动(门或者连续赋值语句),如果没有驱动连接到net,它的值为高阻态(Z);而Register是用来存储数据的变量。一个register存储从一个赋值语句到下一个赋值语句的值。reg型数据的默认值为未知状态(X)。

    1) net型:wire、tri;
     net型相当于硬件电路中的各种物理连接,其特点是输出的值随输入的值而变。
   
     2)variable型:reg、integer。
variable型变量必须放在过程语句(如initial、always)中,通过过程赋值语句赋值;在always、initial等过程块内被赋值的信号也必须定义成Variable型。
     这里需注意的是reg型变量不一定对应这硬件上的寄存器(n bit)或触发器(1 bit)。在综合时,综合器会根据具体情况来确定将其映射成寄存器还是映射为连线。(所以可以这样理解:之所以要定义reg变量,只是因为在过程语句要用到这些变量。不能因为看到reg而片面理解其综合后的结果。)
     integer型变量多用于表示循环变量,这里不用多说。

二、wire和reg的赋值   
      这里继续把wire和reg的赋值分清楚。
      Verilog的赋值分为连续赋值和过程赋值。连续赋值用于数据流行为建模,多用于组合逻辑电路;过程赋值用于顺序行为建模,用于顺序行为建模。
      连续赋值等号右边操作数发生变化就需要执行(上电便一直执行),而过程赋值语句只是执行一次,注意这里的一次是指:在initial块中,过程性赋值只顺序执行一次,而在always块中,每一次满足always的条件时,都要顺序执行一次该always块中的语句。

      分清连续赋值和过程赋值才能理解连续赋值是针对net型来用,过程赋值是针对reg型来用。

     对于reg的过程赋值,又有2种赋值方式:非阻塞赋值和阻塞赋值。阻塞赋值在该语句结束时就立即完成赋值操作,它在initial或always块内是顺序执行的;
      例1:
            always@(A)
              begin
              B=A
              C=B
              D=C
           end
      在这个always块内,当A的值发生变化的时候,会先将A的值赋给B,然后紧跟着把B的值赋给C,接着将C的值赋给D。如果我们没有设定每一个语句的延迟的话,在仿真时我们会看到B、C、D是同时被赋值的。

      例2:
           always@(posedge CK)
              begin
                   M=A&B;
                   Y = M | C;
              end

该电路综合出的电路如下:
11.jpg

这里always块内综合出了组合逻辑和时序器件,这并不是我们所希望的。一个好的coding style需要很清楚地区分二者,在专业的设计领域,只允许always@(posedge clk)这样的语句综合出D触发器。此外阻塞式赋值还可能会有竞争或者组合反馈的危险(是大忌)。这需要绝对避免。来看看2个例子:

       例3:
     always@(posedge clk)
     begin
             //此语句会产生竞争        

         B=A;
         A=B;
     end

     always@(in1, in2, in3)
     begin
             //此语句会产生组合反馈的情况
         temp1 = (in1 | in2) & in3;
         temp2 = temp1 & in2;
         in3 = temp2;
     end

     非阻塞赋值在整个过程块结束时才完成赋值操作,在initial或者always内的非阻塞式赋值语句,并不因为其位置而有先后执行的差别。
      例4:
           initial
             begin
               a<=1; // a<=1和b<=3是同时被执行的
               b<=3;  [url=]//并非执行完a,才执行b[/url]的赋值
             end
       例5:
            always@(posedge clk)
            begin
                M<=A&B;
                Y<=M | C;
            end
   其综合后的电路图如下图:
       11.jpg

  由上可知:对于非阻塞式赋值,综合后的电路会对reg型生成D触发器保存原来的结果。
    (这里说一个题外话,为啥Verilog要这么费劲把数据类型分成wire和reg,为何不直接用variable来表示得了。我想这不单是用来区分组合逻辑电路和时序逻辑电路。更是为综合考虑,即综合时是否在变量前增加触发器或寄存器,是随着输入值时刻变化,还是对变量输出可控。)

综合举例:
    1)对wire型连续赋值(基本RS触发器):(注意wire型的赋值位置)
      module rs_ff(r, s, q, qn);
         input r, s;
         output q, qn;
         assign qn=~(r & q);
         assign q = ~(s & qn);
      endmodule

   2)reg过程赋值(阻塞赋值):(注意reg型的赋值位置)
     module mux2_1_block(out, a, b, sel);
          input a, b, sel;
          output reg out;
         always@(a, b, sel)
             begin
               if(sel == 0)
                     out = a;
               else
                     out = b;
             end
     endmodule

  3) reg过程赋值(非阻塞赋值):见上面的例子。

三、综述
     像C语言的设计围绕编译来考虑一样,Verilog的设计也是围绕着综合来考虑。但相对于C语言,可以说Verilog的设计更严格。从net型和variable型变量就可见一斑。变量不同,对应的综合后的电路就不同;对variable来说,赋值不同,对应的综合后电路也不同。
     我们将阻塞赋值语句的always过程块写成下面的形式。
    always @(event-expression)
       begin
      < LHS1 = RHS1 assignments >  //阻塞赋值语句1
      < LHS2 = RHS2 assignments>   //阻塞赋值语句2
      .
      .
      .
       end
  同样非阻塞赋值语句的always进程块写出下面的形式:
   always @(event-expression)
       begin
      <LHS1<=RHS1 assignments >  // 非阻塞赋值语句1
<LHS2<=RHS2 assignments >   // 非阻塞赋值语句2

     .
      .
      .
       end
其中LHS指赋值符号左边的变量或表达式, RHS指赋值符号右端的变量或表达式。阻塞赋值“=”与非阻塞赋值“<=”的本质区别在于:非阻塞赋值语句右端的表达式计算完后并不立即给左端,而是同时启动下一条语句继续执行,即同时计算RHS1,RHS2,计算完后,在进程结束时,同时分别赋值给左端变量LHS1,LHS2。
     而阻塞赋值语句在每个右端表达式计算完后,立即赋值给左端变量。即赋值语句LHS1 = RHS1执行完后LHS1是立即更新的,同时只有LHS1 = RHS1执行完后才可执行语句LHS2=RHS2,以此类推。
    下面是阻塞语句和非阻塞语句的几条原则:
    (1)当用always块来描述组合逻辑时,既可以用阻塞赋值,也可以采用非阻塞赋值,最好才用阻塞赋值;
    (2)设计时序逻辑电路,尽量使用非阻塞赋值方式;
    (3)描述锁存器,尽量使用非阻塞赋值;
    (4)若在同一个always过程块中既为组合逻辑建模,又为时序逻辑建模,最好使用非阻塞赋值方式;
    (5)在同一个always过程块中,不要混合使用两种赋值方式;
    (6)不能在两个或两个以上的always过程中对同一变量赋值,这样会引发冲突。

看到这里我想可以大话一下两种赋值:阻塞赋值就像工厂一条流水线,各赋值语句是上下游的关系,必须等上游的工序走完才能走到下游。非阻塞赋值像工厂的多条并行的流水线,每条流水线先生产自己的半成品,然后再把半成品加工成成品(赋值到LHS1)。


参考:《数字系统设计与Verilog HDL(第四版)》王金明著
         《精通Verilog HDL:IC设计核心技术实例详解》
          Verilog online help:http://verilog.renerta.com/mobile/index.html

 楼主| 发表于 2012-3-18 15:28:51 | 显示全部楼层
码字不容易,自己先顶一下。。。欢迎IC geek一起交流学习。
发表于 2012-3-18 15:49:25 | 显示全部楼层
自己敲的,有点太累了吧。还是值得肯定。
 楼主| 发表于 2012-3-18 15:54:06 | 显示全部楼层
回复 3# chen.terry


    书上东一块,西一块的,不好理解,整理一下,当是笔记咯。。
发表于 2012-3-18 21:07:31 | 显示全部楼层
谢谢,每一块都能理解,楼主辛苦了。
发表于 2012-3-19 08:38:43 | 显示全部楼层
总结得很好,楼主辛苦了
 楼主| 发表于 2012-3-19 20:13:11 | 显示全部楼层
回复 6# qlengyu


    谢谢,欢迎多交流学习。。。
 楼主| 发表于 2012-3-19 20:14:04 | 显示全部楼层
回复 5# pengch0416


    ,多交流学习。。。
发表于 2012-4-4 14:13:37 | 显示全部楼层
   hdl
发表于 2012-9-20 01:08:45 | 显示全部楼层
非常感谢johnny1209师兄,写的真好。有大家风范的,希望大哥能继续写下去!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-12-19 12:07 , Processed in 0.025094 second(s), 7 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表