FPGA之道(37)Verilog中的编写注意事项

举报
李锐博恩 发表于 2021/07/15 01:52:26 2021/07/15
【摘要】 文章目录 前言Verilog中的编写注意事项大小写敏感Verilog中的关键字范围定义的正确使用不要省略begin与end注释中斜杠的方向编译指令中的前导符号混用阻塞和非阻塞赋值的危害 仿真雷区阻塞赋值顺序敏感量表缺失仿真死循环少用生僻语句 前言 同VHDL一样,Verilog编写已有一些注意事项,但是比较少,下面摘自《FPGA之道》一起看下作者...

前言

同VHDL一样,Verilog编写已有一些注意事项,但是比较少,下面摘自《FPGA之道》一起看下作者如何总结。

Verilog中的编写注意事项

大小写敏感

Verilog是一种case sensitive的语言,即敏感大小写。例如以下几个变量是不一样的:
reg abc;
reg Abc;
reg aBc;
reg ABC;
在verilog中,它们分别代表4个不同的寄存器类型,不能搞混。如过定义了一个寄存器类型变量a,但是使用的时候写成A,编译器会报错,表示没有找到A的定义。

Verilog中的关键字

Verilog中的命名也不能与关键字相同, 所以在书写代码的时候也要避免使用了关键字作为标识符。以下列举了Verilog中的一些关键字供大家参考,可以看出Verilog中的关键字都是小写的,由于Verilog是大小写敏感的,所以只要标识符不完全和关键字一样即可。

always	and	assign	attribute	begin	buf	bufif0	bufif1	case	casex	casez
cmos	deassign	default	defparam		disable	edge		else	end	endattribute
endcase	endfunction	endmodule	endprimitive	endspecify	endtable	endtask
event	for	force	forever	fork	function	highz0	highz1	if	ifnone	initial
inout	input	integer	join	medium	module	large	macromodule	nand
negedge	nmos	nor	not	notif0	notif1	or	output	parameter	pmos
posedge	primitive	pull0	pull1	pulldown	pullup	rcmos	real	realtime	reg
release	repeat	rnmos	rpmos	rtran	rtranif0	rtranif1	scalared	signed
small	specify	specparam	strength	strong0	strong1	supply0	supply1	table
task	time	tran	tranif0	tranif1	tri	tri0	tri1	triand	trior	trireg	unsigned	vectored	wait	wand	weak0	weak1	while	wire	wor	xnor	xor  

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

不过也恰恰由于Verilog是大小写敏感的,所以在使用关键字时切忌要保持全部小写,例如,如下的使用都是错误的:
REG a; // wrong, should be “reg”
Wire b; // wrong, should be “wire”
aSSign b = 1’b1; // wrong, should be “assign”

范围定义的正确使用

在Verilog中,声明端口或者变量时,往往都需要配合语法来说明位宽信息,例如:
wire [15:0] x;
不过下述定义也是正确的,
wire [0:15] y;
即范围可以从大到小,也可以从小到大。但是像上述这样的定义往往会让人犯迷糊,从而导致使用的时候出现很多问题。为了能够正确使用,减少出问题的机率,我们列举出范围参数的详细语法:
[MSB:LSB]
从范围参数的详细语法我们可以看出,范围参数的左边界是对应MSB的,右边界是对应LSB的。所以,如果一个二进制数c按照从左至右,从MSB到LSB排列,为
c15、c14、c13……、c2、c1、c0;
那么如果做如下操作:
assign x = c;
assign y = c;
则x[15] = c15, x[0] = c0; y[0] = c[15], y[15] = c0;
记住这一对应关系即可自如应对范围定义不统一的问题,不过建议大家还是使用从大到小的范围定义法,因为这和二进制数的排列是一致的,方便理解和编程,并且在初始化数组时,有时候只有按照这种方法定义的数组才能初始化成功。
除此以外,在早期版本的Verilog语言中,范围参数中不允许出现变量,当需要根据变量来选择范围的时候,只能通过单个索引的方式来完成,例如:

a = b[i:i-3]; //wrong
需要改成
a[3] = b[i]; 
a[2] = b[i-1]; 
a[1] = b[i-2]; 
a[0] = b[i-3];

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

不过在Verilog2001版本以后,Verilog支持了在范围中使用变量,并且还引入了新的语法如下:

[<base_index> +: bit_width]  //由低位到高位
[<base_index> -: bit_width]  //由高位到低位

  
 
  • 1
  • 2
例如:

  
 
  • 1
wire [7:0] b;
wire [3:0] a = b[6 -: 4]; //equal to a= b[6:3];
integer i;
reg [3:0] c;
c <= b[i -: 4]; //must in always

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

不要省略begin与end

在Verilog中,很多语句都可以包含一些子语句,例如always程序库、if条件、for循环等等。一般情况下,如果某个分支中子语句的数量仅有一条的时候,可以省略begin-end语法。例如:
always@*
if(sel == 1’b1)
a = b;
else
a = c;
不过,为了保证代码的一致性和可读性,更为了减少出错的可能,建议大家不要省略begin-end语法。这样虽然代码看起来略微长一些,但是无论是从修改还是从阅读来说都会给我们带来极大便利。例如上例改写如下:
always@*
begin
if(sel == 1’b1)
begin
a = b;
end
else
begin
a = c;
end
end
顺便提一下,唯一不能省略begin-end语法的是循环生成语句。

注释中斜杠的方向

Verilog的注释符号有两种:
“//”和“/* … */”
请注意斜杠的方向为键盘中和“?”共用一个键位的斜杠符号,而不是反斜杠“\”。

编译指令中的前导符号

使用编译指令时,都需要在前面加一个前导符号“”,并且在引用宏定义中参数的时候也需要加这个符号来表明。注意,该符号不是单引号“’”,而是位于键盘左上角和“~”符号共用一个键位的“”符号。

混用阻塞和非阻塞赋值的危害

如果在一个always程序块中,混用阻塞与非阻塞赋值,轻则会造成组合逻辑和时序逻辑混杂在一起的结果,给人带来很大的混乱;重则直接影响仿真或编译的正确性。
如果给一个变量混用混用阻塞与非阻塞赋值,那么结果更是不可预测,估计这个时候你自己都不知道你是希望它被综合工具翻译成连线还是寄存器,此时只有期待老天保佑了!

仿真雷区

阻塞赋值顺序

从阻塞赋值的意义我们就可以得知,如果代码书写的顺序改变,那么程序的功能就可能会有变化。这尤其对仿真器影响最大,因为仿真器是完全按照Verilog语法来理解程序的。
例如:
always@(a, b)
begin
t = a & b;
c = ~ t;
end
若写成这样仿真就会出现问题,即每次a或b改变时,c得到的是上一次a、b的与非结果:
always@(a, b)
begin
c = ~ t;
t = a & b;
end
若要保证仿真行为正确,可以在敏感量表中添加本来不是输入变量的变量t。但是带来的负面影响就是always必须执行两次才能得到正确的结果。
always@(a, b)
begin
t = a & b;
c = ~ t;
end

敏感量表缺失

敏感量表的缺失问题一般指的都是组合逻辑always,因为时序逻辑的敏感量表比较固定,一般不会出问题。如果敏感量表缺失,那么其实从语法上来看是会引入锁存的,但是目前的编译器一般都会帮助客户补全敏感量表从而避免锁存的出现,而仿真器没有该功能,所以如果敏感量表缺失,是会影响到仿真结果的。例如:
always@(a)
begin
t = a & b;
c = ~ t;
end
此时,若b改变,c不会改变,因为仿真器认为该程序块不敏感b的事件。

仿真死循环

组合逻辑如果表述的不好,很容易造成仿真死循环,例如,类似如下的代码都是有问题的:
always@*
a = not a;

assign b = m + b;
以上反馈如果出现在时序逻辑中,不会有任何问题,因为时序逻辑中的赋值要到下一个周期才会生效。但是如果用在组合逻辑中,由于赋值马上生效,一旦生效后的值与之前的值不同,又会激发下一次赋值,如此往复而无穷尽,所以仿真器会卡在这一时刻无法走下去。
当然了,并不是说一定不能存在反馈,而是不能存在负反馈,例如SR锁存器就可以利用正反馈来实现稳定的电路变化。虽然实际中有存在负反馈的数字组合逻辑电路,但是它得到的结果对数字逻辑来说是没有意义的,因此在编程时请注意避免。

少用生僻语句

正如我们在本篇开头所说,语言在不断发展,仿真器也在不断发展,那么肯定是越常用的语法,越规范的语句,仿真器支持的越好,那些比较生僻的语句,即是仿真器支持,编译器还不一定支持,所以,建议大家多多使用常用的语法结构进行编程,请放心,这完全不会影响你去实现一个非常非常复杂的设计,反而这才是最好的做法。

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/104305242

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。