verilog HDL语法总结

verilog HDL总结 (2010-09-16 16:38:12)转载▼

标签: 杂谈 分类: EDA

1. Assign 语句中赋值的变量不能定义为reg型。

2  Always块里面写的是触发信息。

3  测试模块里要包含所有的情况。如果可以先把波形图画出来,再按照波形图写测试模块会好写些,并且能写全。

4 在进行测试模块编程时,输入定义为寄存器类型,输出定义为线性。

5 信号初始化放在initial中。

6 可以用计数来实现分频。比如根据时钟的周期计算出记一个数所用的时间。高电平的时间内计多少数,低电平的时间记多少数。高低电平时间之和加起来就是周期。再求倒数就是频率。

7  always模块中被赋值的语句的变量应定义为reg型。

8  被测试模块的输出信号定义为wire型。被测试模块的输入信号定义为reg型。

9  If块里面的语句如果有两句以上,要加上begin end 否则会出错。

else 里面的语句如有有两个以上,也要加上begin end

10  If 语句中给RB赋值,要写成非阻塞赋值,RB=0;如果写成阻塞,报错。

11 一般情况下,要有一个总体系统的复位信号,否则,由于初始状态不定,所以仿真时,第一个周期容易不准确

12  Always模块里面语句是时刻赋值,意思是,比如一个上升沿作为触发信号, 如果在模块里面你写f0=f1,你的意思是想把信号f1赋给f0,这是错误的,它只能将f1此刻的值赋给f0

13. if 可以和else不配对使用,if可以单独使用

14If  else 里面可以再嵌套if else

  Eg  if(MSL==9)

        begin

              if(MSH==9)

                  begin

                    MSH<=0;cn1<=1;

                  end

              else

                    MSH<=MSH 1;

        end

     else

        begin

           MSL<=MSL 1;cn1<=0;

        end

    15. else if 里面仍然可以嵌套if   else语句

Eg                 

   else if(SL==9)

      begin

              if(SH==5)

                 begin

                   SH<=0;cn2<=1;

                 end

             else

                  SH<=SH 1;

      end

     

     else

    

      begin

         SL<=SL 1;cn2<=0;

      End

 

16. 如果问题老是出在第一行,很可能是没有写endmodule

17.每个端口除了要声明是输入,输出,还是双向端口外,还要声明其数据类型

是连线性还是寄存器类型。如果没有声明,综合器默认为wire

18. 输入和双向端口不能声明为寄存器类型

19.调用子模块时,里面变量的例化写法要一致。

 COUNT    U2(.RB(RBB),.CLK(CLK),.EN(FD),.Q(Q)); 是正确的

    COUNT    U2(RBB, CLK,.EN(FD), Q);是错误的。

20. reg    [3:0]MSH,MSL,SH,SL,MH,ML=0;表示的是只将ML0,其他都没有赋0

 

 

 

verlog语法要点:

     module endmodule之间由两部分构成:接口描述和逻辑功能描述

     IO端口种类: input  output  inout

     相同位宽的输入输出信号可以一起声明, input[3:0] a,b; 不同位宽的必须分开写

     内部信号为reg类型,内部信号信号的状态: 0  1  x   z,  3'bx1=3'bxx1 x/z会往左扩展  3'b1=3'b001 数字不往左扩展

     逻辑功能描述中常用assign描述组合逻辑电路,always既可以描述组合逻辑电路又可以描述时序逻辑电路,还可以用元件调用方法描述逻辑功能

     always之间、assign之间、实例引用之间以及它们之间都是并行执行,always内部是顺序执行

     常量格式: < /-><二进制位宽><'><进制><该进制的数值>

          默认进制为10进制

          默认位宽为32

          位宽是从二进制宽度角度而言的

          由位宽决定从低位截取二进制数2'hFF=2'b11,通常由被赋值的reg变量位宽决定

     parameter常用于定义延迟和变量位宽,可用常量或常量表达式定义

  变量种类: wire reg  memory

          IO信号默认为wire类型,除非指定为reg类型

          wire可以用作任何输入输出端口

          wire包括input output inout

          wire不带寄存功能

          assign赋值语句中,被赋值的信号都是wire类型

          assign之所以称为连续赋值,是因为不断检测表达式的变化

          reg类型可以被赋值后再使用,而不是向wire一样只能输出,类似VHDL中的buffer端口

          reg类型变量初始值为x (VHDL中初始值为本类型最小值,通常是0)

          always模块里被赋值的信号都必须定义为reg类型,因为always可以反复执行,而reg表示信号的寄存,可以保留上次执行的值

          reg类型变量与integer变量不同,即使赋负值,实质上也是按二进制无符号数存储的,integer是有符号数

          verilog中所有内部信号都是静态变量,因为它们的值都在reg中存储起来了

          memory型只有一维数组,由reg型变量组成

          memory初始化只能按地址赋值,不能一次性赋值

          1*256memory写法: reg mema[255:0]     mema[3]=0;

          不同位宽的变量之间赋值,处理之前都以被赋值的变量位宽为准扩展或截取

          A[a:b] 无论a b谁大,a总是实际电路的信号高位,b总是实际电路的信号低位

          算术运算中如果有X值则结果为X

          for循环中的变量另外定义成integer,因为它不是实际信号,有正负;reg则以无符号数存在

     == !=只比较01,遇到zx时结果都为x (xif中算做假条件),结果可能是10x

     ===!==比较更加苛刻,包括xz的精确比较,结果可能是01

     &&的结果只有1'b11'b0两种, A&A的结果位宽则是与A相同的

     {1,0}64'h100000000,所以拼接运算中各信号一定要指定位宽

     移位运算左移将保留 4'b1000<<1等于5'b10000,右移则舍弃 4'b0011等于4'b0001

     数字电路里位运算应用普遍,包括按位逻辑运算、移位运算、拼接运算、缩减运算

   

     非阻塞式赋值<=与阻塞式赋值=

         阻塞:在同一个always过程中,后面的赋值语句要等待前一个赋值语句执行完,后面的语句被该赋值语句阻塞

         非阻塞:在同一个always过程中,非阻塞赋值语句是同时进行的,排在后面的语句不会被该赋值语句阻塞

         <=

             块结束后才能完成赋值

             块内所有<=语句在always块结束时刻同时赋值

             <=右边各变量的值是上一次时钟边沿时,这些变量当时的值

             用于描述可综合的时序电路

         =

             =语句结束之后过程always才可能结束

             always过程中,begin end块内按先后顺序立即赋值,在fork join内同时赋值(可能造成冲突)

             assign连用描述组合电路

             begin end中阻塞的含义:begin  ...@(A) B="C"...;  end   如果A事件不发生则永远不能执行下去,被阻塞了

         由于时钟的延时(往往在ps),多个always(posedge)之间究竟谁先执行是个未知数

         使用原则:同一个always过程块内建立时序电路用<=

                    纯组合逻辑电路用=,生成的电路结构最简单,执行速度最快

                    同一个always块内不要混用<==

                    不要在多个always块内对同一个变量赋值(多源驱动)

      

     if else的三种形式,第三种形式适合描述优先编码器

     if条件中0/x/z当成假,1当成真,非0的数值也当成真

     case语句的三种: case(四种状态的比较)  casez(忽略z)  casex(忽略xz,只看哪些位的信号有用)

     case语句中所有表达式值的位宽必须相等,default中不能将n'bx'bx代替

     避免生成锁存器的方法: 电平触发时if后加else   case中加default    

     使用casex会将不必要的状态视为无关项,使得综合出来的电路最简单

     两种特殊的括号: begin 顺序语句... end     fork 并行语句... join,其差别在于块内语句的起止时间、执行顺序、相对延时

  

     块被命名后,其内部变量可以被调用,因为变量都是静态的(调用信号:对应电路中的一个信号线被引到另一处)

  

     initial块只无条件执行一次  always块在满足条件时不断执行

     initial常用来写测试文件, always块常用来写电路描述

     always既可以描述组合逻辑电路又可以描述时序逻辑电路

     always如果后面有敏感信号列表则不能用wait语句

     always既可以描述电平触发又可以描述边沿触发,wait只能描述电平触发

     assign常用于描述组合逻辑电路

     测试文件中一般都是现initial always

 

     生成语句:生成快的本质是使用循环内的一条语句代替多条重复的verilog语句,简化了用户的编程

               genvar用于声明生成变量,生成变量只能用在生成快之间

               仿真时,仿真器会将生成块中的代码展平,在确立后的方针代码中,生成变量是不存在的

               最好是先想象出来循环生成语句被展平后的电路样子,再写相关的描述语句

 

     taskfunction的区别:

               task可以定义自己的仿真时间单位,function与主模块共用同一个仿真时间单位

               函数不能启动任务,任务能够启动函数

               函数至少要有一个输入变量,任务没有输入变量

               函数返回一个值,任务不返回值

  

     一个模块的设计包括3个部分: 电路模块的设计  测试模块的设计  设计文档的编写

     设计者通过布局布线工具生成具有布线延迟的电路,再进行后仿真,得到时序分析报告

     从时序分析报告中可以知道电路的实际延迟t,同步电路内每个时钟周期要大于t,从而可确定该运算逻辑的最高频率

     综合器之所以能够实现加法器、乘法器是因为库中已经存在可配置的参数化器件模型

     FPGA内总线宽度容易自定义,以便实现高速数据流,三态数据总线相当于数据流的控制阀门

     数字系统内数据流的控制: 开关(或三态数据总线)、数据暂存部件(寄存器)、 同步状态机控制(整个系统在一个时钟域内)

  

     流水线操作pipe line

         K级流水线就是从组合逻辑的输入到输出恰好有K个寄存器组,上一级的输出是下一级的输入

         流水线操作获得第一个结果的时间要比不用流水线操作的时间长,但以后结果获得时间都只需要一个时钟周期,提高了数据吞吐量

         流水线操作的保证:Tclk>K*(组合逻辑延迟 触发器的建立保持时间/触发时间),即时间片段要长于最大路径延迟

         体现了面积换速度的思想,在综合时考虑的是以面积小为主还是以速度为主

         本质上是一种同步逻辑

  

     同步时序逻辑和异步时序逻辑:

         同步时序逻辑指所有寄存器组由唯一时钟触发   always@(posedge clk)  always@(negedage clk)

         异步时序逻辑指触发条件不唯一,任意一个条件都会引起触发  always@(posedge clk or posedage reset)

         目前的综合器是以同步时序逻辑综合的,因为同步时序逻辑较异步时序逻辑可靠

         严格的同步要求时钟信号传递速度远远大于各部分的延迟,实际中clk要单独用线,而不要经过反相器等部件

         always @(posedge.. )  begin ...<=... end  表示同步时序逻辑(同时刻赋值)

         不同速率数据接口的处理方法(异步数据的处理方法):帧同步    FIFO  双端口RAM

  

     同步状态机:

         包括mooremealy型两种,及其反馈模型(是一种反馈控制系统,当前状态就是其内部状态变量)

         状态机的开发步骤:

             根据实际问题列出输入输出变量和状态数

             画出状态图并化简

             写出状态转移真值表得到逻辑表达式

             D触发器或JK触发器构建电路(目前用D触发器多)

         verilog描述时只需要得到简化的状态图就可以描述

         状态编码方式: 独热码   格雷码

         状态机主体程序有单always描述方式和多always描述方式

         采用case/casez/casex建立模型最好,因为x是无关态,生成的电路最简单

         default: state='bx与实际情况更一致,效果等同于 default: state<=idle

         只有同步状态机才能被目前的综合

 

 

for语句会将所有变量的情况展开,占用巨量逻辑资源,替代办法是用计数器和case语句说明所有情况

有优先级的if else结构会消耗更多资源,建议用无优先级的case替代

模块的复用往往比代码上修改节省的资源多

PLL的分频、倍频、移相操作会增加设计精度

同步时序电路的延时:#x通常用于仿真测试,实际硬件延时是:长延迟用计数器,小延迟用D触发器,此方法用来取代延迟链

同步电路中,稳定的数据采用必须满足采样寄存器的建立和保持时间

reg类型在always中不一定综合成时序电路,也可能是组合逻辑电路

乒乓操作与作用  异步时钟域同步问题

延迟包括门延迟和线延迟

组合逻辑产生的时钟仅能应用在时钟频率较低、精度要求不高的情况下

增减敏感信号得到的结果一样

 

 

补充部分:

 

verilog HDL起初是作为写testbench而产生的

verilog 1995进入IEEE标准,为IEEE-1364, 2001年进行了扩展,为IEEE 1364-2001

verilog AMS可用于模拟电路和数字电路的综合,目前正在不断发展和完善中;

 

verilog的标识符区分大小写,关键字使用小写;

\\来进行单行注释,用\* *\来进行跨行注释;

标识符由字母、数字、下划线构成,并以字母开头;

关键字又叫保留字,只有小写的关键字才是保留字;

 

信号的状态有4种: 0 1 x z

xz在描述电路时不区分大小写,在仿真时大小写有不同意义;

 

常量表达式中:

x z不区分大小写;

进制符号h o d bH O D B不区分大小写;

十六进制中a~f不区分大小写;

下划线_用于提高可读性;

?在数中可以代替z

xz的左端补位;

 

字符和字符串都以ASICII码形式存在,也可以当成电路内的信号;

字符串必须包含在同一行,不能分成多行书写;

如果表达式或者赋值语句中将字符串当成操作数,则字符串中的每个字符都被看成8位的ASCII值序列;

 

可综合的信号类型:wire reg memory 它们用来描述数字电路

不可综合的数据类型:integer real 它们只用仿真,位于testbench

 

wire是连线的抽象模型,不能保存数据,其值由驱动元的值决定;

wire不能用在alwaysinitial块中;

wire的默认值为高阻z

wire的使用情形: 1.作为模块的输出端口  2.用连续赋值语句assign赋值;

reg1位寄存器(触发器)的抽象模型,可以保存数据;

reg必须用在alwaysinitial块中;

reg的默认值为x

reg的使用情形:1.阻塞赋值<= 2.非阻塞赋值=

memory只能是一维的;

memory只能对每个单元分别初始化,方法:1.一个一个赋值  2. 通过系统任务$readmem赋值

reg[3:0] fc//一个4位寄存器   reg fc[3:0]  //4个一位寄存器

 

parameter的作用:仿真开始以前对其进行赋值,整个仿真过程中保持其值不变;

 

关系运算符将以逻辑1或逻辑0返回比较的结果;

== !=的返回值有0 1 x三种情况,=== !==的返回值只有0 1两种情况;

 

verilog由于是描述电路的,用于位的操作较多,有: 位逻辑操作,移位操作,并置操作,归约操作;

位逻辑运算的结果中,位数与原操作数一样多;

归约符是在原操作数的所有位上进行操作,并产生1位结果;

并置运算可以发生在bitbit之间 bit与矢量之间 矢量与矢量之间

 

用于仿真的系统任务:

所有系统任务都必须在initialalways内;

所有系统任务都必须以$开头;

常见系统任务:

  显示任务($diplay系列和$write系列)

  监控任务($monitor系列)

  探测任务($strobe系列)

  文件打开、输入、关闭任务(&fopen &fclose &fdisplay...)

  读取文件任务($readmemb $readmemh)

  仿真结束控制任务($finish  $stop)

  随即信号任务($random)

 

过程块: initial块和always

一个module内可以包含多个initialalways模块;

所有initialalways块在0时刻开始并行执行,各initialalways块内部顺序执行;

initial过程块主要是面向testbench的,通常不具有可综合性;

always过程块在描述电路时既可以描述组合逻辑电路(电平敏感)又可以描述时序逻辑电路(边沿敏感)

testbenchinitial通常用于初始化以及顺序波形的描述,always通常用于重复波形的描述;

 

任务task与函数function: 为了描述模块中被多次执行的部分以及为了增强代码的易读性

verilog中的高级程序语句如for循环语句只用在写testbench中;

begin endfork join是两种特殊的括号

if语句的第三种形式适合描述优先编码器,case语句适合描述数据选择器和状态机;

 

case的条件表达式如果与分支项表达式长度不同,则在比较前将所有表达式都统一为这些表达式的最长长度;

casez忽略zcasex忽略zx

 

assign语句只在右端表达式发生变化时才重新计算并重新赋值,其余时间都是连续赋值;

assign语句可以指定bitvector或是任意拼接操作的结果;

assign语句是连续赋值的,用于驱动网线wirereg类型不需要连续赋值,reg类型一旦被赋值就会一直保存;

 

过程赋值语句有两种:阻塞式=和非阻塞式<=,只能在过程块initialalways中使用;

@对事件触发的控制与wait语句不能同时使用;

你可能感兴趣的:(fpga)