首页 > 代码库 > 关于Verilog HDL的一些技巧、易错、易忘点(不定期更新)

关于Verilog HDL的一些技巧、易错、易忘点(不定期更新)

   本文记录一些关于Verilog HDL的一些技巧、易错、易忘点等(主要是语法上),一方面是方便自己忘记语法时进行查阅翻看,另一方面是分享给大家,如果有错的话,希望大家能够评论指出。

关键词:

  ·技巧篇:

      组合逻辑输出类型选择;

      语法上的变量交换;

 

  ·易忘篇:

      case/casex/casez语句;

      循环语句;

      数制和操作符;

      数据类型;

 

  ·易错:

 

技巧篇:

  1、组合逻辑输出:描述一个纯组合逻辑电路时,尽量不要把输出定义成输出类型,例如描述下面的电路:

 技术分享

 1 module mux #(parameter N=2)( 2  3 input [N-1:0] a, // sel=00时,选择该输入 4  5 input [N-1:0] b, // sel=01时,选择该输入 6  7 input [N-1:0] c, // sel=10时,选择该输入 8  9 input [N-1:0] d, // sel=11时,选择该输入10 11 input [1:0] sel, //选择器12 13 output[N-1:0] mux_out);// 选择器结果输出14 15 reg [N-1:0] mux_temp; // 临时变量,用于防止其他调用者误认为输出锁存16 17 assign mux_out=mux_temp;18 19 //always_comb //该语句在systemverilog中可以替换下面的语句并检查20 21 always @ (a or b or c or d or sel)22 23   case (sel)24 25     0 : mux_temp = a;26 27     1 : mux_temp = b;28 29     2 : mux_temp = c;30 31     3 : mux_temp = d;32   33     default : $display("Error with sel signal");34 35   endcase36 37 endmodule

 

 

 

  2、语法上的变量交换:在always 语句块内部,任何一个语句块(以begin 开始,end 结束)都是串行执行的,只是存在赋值立刻生效还是事后生效的差异,即后面将要重点论述的阻塞赋值和非阻塞赋值两种区别(这两种赋值语句综合的区别请看我的另一篇博文,链接为:

        http://www.cnblogs.com/IClearner/p/7188875.html)。

对于下面的代码,从纯语法上讲:

1 always@(*)begin2 3     temp=b;4 5     b=a;6 7     a=temp;8 9 end

 

上面的例子,就是一个串行执行的例子,能够完成 a 与 b 的数值交换,如果不是串行执行,上述代码将很难完成类似各类程序控制。

 

 

易忘篇/陌生篇:

 

  1、case语句的各种注意情况及对应综合电路

   (留坑,以后填)

 

  2、循环语句:循环语句,主要包含 for、while、forever、repeat 四类语句,但只有 for 语句才有可能具备可综合性,其余均为测试验证所准备。

循环语句 for 的语法为:

    for(表达式 1;表达式 2;表达式 3) 语句

其实可以将 for 语句理解为:

    for(循环变量赋初值;循环结束条件;循环变量增值)执行语句

·for 循环的例子如下(截取关键部分,非完整代码),这是最原始的一个8bit 乘法器实现,其中<<表示左移,等效于乘以2 的移位次方:

 1 parameter size = 8; 2  3 reg[size-1:0] opa, opb; 4  5 reg[2*size-1:0] result; 6  7 integer bindex; 8  9 always @(*)begin10 11     result =  opb[0]?opa:0;12     13     for( bindex=0; bindex<=size-1; bindex=bindex+1 )begin//根据乘法特性,判断后是否进行移位14 15         if(opb[bindex]) result = result + (opa<<(bindex));16 17      end18 19     mult_out = result;20 21 end    

 

(上述例子也可以当做技巧看,也就是使用位移实现乘法运算)

 

  3、数制与操作符

  这里数制和操作符...其实我已经基本是滚瓜烂熟了,放在这里是给初学者查询的...

用例

说明

‘hAE

8 位十六进制数

10‘b10

左边添 0 占位,实际为 10‘b0000000010

10‘bx1x0

左边添 x 占位,实际为 10‘bxxxxxxx1x0

3‘b1001_0011

3‘b011 相等

 

 

运算类别

符号

运算符含义

算术运算符

+

加法(二元运算符)

-

减法(二元运算符)

*

乘法(二元运算符)

/

除法(二元运算符)

%

取模(二元运算符)

关系运算符

>

大于

<

小于

>=

不小于

<=

不大于

==

逻辑相等

!=

逻辑不等

逻辑运算符

&&

逻辑与

||

逻辑或

逻辑非

按位逻辑运算符

~

一元非,相当于非门运算

&

二元与,相当于与门运算

|

二元或,相当于或门运算

^

二元异或,相当于异或门运算

~^,^~

 

二元异或非即同或,相当于同或门运算

 

 

移位运算符

>>

右移

<<

左移

赋值运算符

=

阻塞赋值,等效于立即生效

<=

非阻塞赋值,等效于当前模块结束后赋值,或者下个时钟

 

周期赋值生效

缩减运算符

&

一元与,相当于数据 bit 逐个进行与操作

|

一元或,相当于数据 bit 逐个进行或操作

^

一元异或,相当于数据 bit 逐个进行异或操作

~^

一元同或,相当于数据 bit 逐个进行同或操作

      单元运算符:可以带一个操作数,操作数放在运算符的右边。

      二元运算符:可以带二个操作数,操作数放在运算符的两边。

      三元运算符:可以带三个操作数,这三个数用三目运算符分隔开。

  缩减运算符是对单个操作数进行或与非递推运算,最后的运算结果是一位的二进制数。缩减运算符目前支持或与非三种操作。具体运算过程如下:第一步先将操作数的第一位与第二位进行或与非运算,第二步将运算结果与第三位进行或与非运算,依次类推,直至最后一位。

  拼接运算符则与缩减运算符相反,主要目的是将两个或多个信号的某些位拼接起来进行运算操作。拼接运算不消耗任何逻辑资源,只是一个单纯的连线逻辑。其使用方法如下:

    {信号1的某几位,信号2的某几位,..,..,信号n的某几位}

即把某些信号的某些位详细地列出来,中间用逗号分开,最后用大括号括起来表示一个整体信号。例如:

    {a,b[3:0],c,3‘b101}

也可以写成如下形式:

    {a,b[3],b[2],b[1],b[0],c,1‘b1,1‘b0,1‘b1}

在位拼接表达式中不允许存在没有指明位数的信号。这是因为在计算拼接信号的位宽的大小时必需知道其中每个信号的位宽。位拼接还可以用重复法来简化表达式,例如:

    {6{a}}//这等同于{a,a,a,a,a,a,a},a可为任意比特位宽

位拼接还可以用嵌套的方式来表达,例如:

    {c,{3{a,b}}}//这等同于{c,a,b,a,b,a,b}

用于表示重复的表达式如上例中的6 和3,必须是常数表达式或者参数

 

 

  4、数据类型:

  ·当一个wire 类型的信号没有被驱动时,缺省值为Z(高阻)。

 

  ·有一种专门针对存储器模型(RAM)的定义方法,例如:

    (* ramstyle ="MLAB"*)reg[31:0] RegFile1[15:0];

    (* ramstyle ="MLAB"*)reg[31:0] RegFile2[15:0];

  在ASIC 设计中,这种描述方式只会被识别为一系列的寄存器堆,并不会被识别为RAM;ASIC 中应当利用RAM 单元库(IP)例化的方法描述RAM。而在FPGA 中,综合器首先将这种描述识别为RAM 的声明,并通过识别对象的行为确认描述对象是RAM 还是寄存器堆。如果后续的描述行为满足RAM 的特征,就自动替换为FPGA 内部内置的RAM 单元库,否则将识别为寄存器堆。上例的RegFile1RegFile2 对象在Altera FPGA 中,将被识别为16 个32bit 位宽的RAM,而且指定为MLAB 类型。

 

 易错篇

 (留坑,以后有萝卜再填)

 

 

关于Verilog HDL的一些技巧、易错、易忘点(不定期更新)