用Verilog HDL编写的设计模块最终要生成实际工作的电路,因此,设计模块的语法和编写代码风格会对后期电路产生影响,所以,若要编写可实现的设计模块,就需要注意一些问题
可综合语法
可综合的设计是最终实现电路所必需的,所以弄清哪些语法是可综合的,哪些语法是不可综合的非常有必要,而且设计者也必须知道一个代码能否被综合成最终电路;
例如:写一个简单的除法a/b,想妄图直接通过综合工具生成一个除法器是不现实的,还有有符号数和浮点数的时候也需要注意。
总之,设计者的思路定要从软件角度转变到硬件角度,很多在软件中可以直接使用的情况到了硬件电路就需要从很底层的角度来编写。
可综合的语句有:
1)module 与 endmodule 模块声明的关键字
2)输入input,输出output和双向端口inout的声明
3)变量类型reg,wire,integer
4)参数parameter和宏定义define
5)所有的Verilog HDL内建门,如:add,or之类的门
6)数据流语句assign语句
7)行为级中敏感列表支持电平和边沿变化,类似posedge,negedge
8)always,function可以被综合,task中如果不含延迟可以被综合
9)顺序块begin……end可以被综合
10)if和 case语句可以被综合
不可被综合的语句
在Verilog HDL中不可被综合的语法这里也简单列出来:
(1)初始化initial结构不能被综合,电路中不会存在这样的单元。电路中一旦通电就会自动获得初始值,除此之外时序电路可以用复位端完成初始化组合,电路不需要初始化
(2)#带来的延迟不可被综合。电路中同样也不会存在这样简单的延迟电路,所有的延迟都要通过计时电路或交互信号来完成
(3)并行块fork…join不可被综合,并行块的语义在电路中不能被转化
(4)用户自定义原语UP不可被综合
(5)时间变量time和实数变量real不能被综合
(6) wait ,event , repeat ,forever等行为级语法不可被综合
(7)一部分操作符可能不会被综合,例如,除法/操作和求余数%操作
- 补充:
综合工具也在不断更新和加强,有些现在不能被综合的语法慢慢地会变得可以综合,像比较简单的initial结构在一些 FPGA工具中也可以被识别,同时能被转化为电路形式
。
而有些语句是由于语法特点被综合工具限制,比较典型的就是for语句。
for循环语句简洁明了,编写代码非常方便,但在综合过程中会被完全展开,如 for(i=0;1<9; i=i+1)这条语句在综合工具中就会被展开成10个语句并形成10个相似的电路,这些电路都会出现在最终的电路图里,造成电路规模展开过大。而且for循环中的i一般都比较大,这样展开的效果就更加明显。
但使用for的时候设计者的思路其实是想要通过一个简单的电路完成判断,然后执行 for所包含的语句,这样设计者和综合工具之间的处理过程不一样,只能以综合工具为准。 - 注意:
不可综合的语句在仿真工具中是编译不出来的
因为仿真工具只能检查仿真相关的语法,不能考虑后期综合电路的情况,而仿真所用的测试模块没有语法限制,所以无法提供可综合语法的帮助。
Verilog代码风格
阻塞与非阻塞
使用的赋值类型依赖于所描述的逻辑类型:
- 在
时序块RTL代码中使用非阻塞赋值
,非阻塞赋值保存值直到时间片段的结束,从而避免仿真时的竞争情况或结果的不确定性; - 在
组合的RTL代码中使用阻塞赋值
,阻塞赋值立即执行 - 当用
always块为组合逻辑建模
,使用“阻塞赋值”
; - 当在
同一个always块里面既为组合逻辑又为时序逻辑建模
,使用“非阻塞赋值”
; - 不要在同一个 always 块里面混合使用“阻塞赋值”和“非阻塞赋值”
多重驱动问题
多重驱动问题是初学者最容易犯的错误之一,主要原因就是逻辑划分不清。
在可综合的模块中,一个信号的赋值只发生在一个always结构中
,如果出现在两个always 结构中就构成了多重驱动
,综合工具会认为这两个电路会尝试对同一个变量赋值,实际效果就会造成电路信号的碰撞,然后生成无法预料的结果。
所以设计者在设计模块的时候一般都会在一个always结构中把某个输出的所有情况
都写清楚,确保没有考虑不全的情况,然后再去编写其他输出的情况。
多重驱动问题一般发生在有多个判断条件的情况时,此时的设计思路不要考虑“在这些情况下设计模块的输出都应该是什么”,而是要考虑“每个输出在这些情况下都应该输出什么”,也就是不要从情况人手,而要从输出的角度来看待电路
。
而在Verilog HDI编写设计模块的语法指导中也建议设计者每个always结构完成一个信号的赋值,除非几个信号产生变化的情况都相同或者信号之间有强烈的依赖关系时才放在一起。
敏感列表不完整
- 在
@
引导的敏感列表中必须包含完整的敏感列表,这是针对组合逻辑电路
而言的。 - 时序电路中
@
的敏感事件只是clock的边沿或reset一类信号的边沿情况,若出现其他变量就会变成异步电路,而异步电路的设计很多综合工具并不支持或支持得很差,需要人工帮助。
组合逻辑电路敏感列表不完备就会造成仿真结果不正确,以及最终实现的电路结构不正确或出现锁存器结构。
例如:
always @(a)
c=a^~b;
这个代码中希望生成的是同一个电路,但是敏感列表缺少b,
这样b的变化不会促使always结构发生变化。
此代码综合后可能会生成一个带控制端的锁存器的电路形式,当然也可能是正确的,但设计者不能过分依赖综合工具。
把敏感列表补充完整如下:
always @(a or b)
c=a^~b;
if-else不成对出现
例如:
reg [1:0]out;
always @(posedge clock)begin
if(s==2'b00)out<=2'b00;
if(s==2'b11)out<=2'b11;
end
存在的问题:
-
代码优先级问题
指两个if先判断哪一个?如果电路整体资源情况包括时序、面积等比较充裕,先执行哪个都可以,但如果电路资源情况比较紧张,这两个if语句的先后就可能决定了时序上的成功和失败。
因为这两个if语句最后实现电路的情况完全由不同的综合工具自已定义,最终电路不可确定。
- if…else问题
出现了一个 if
,必然要出现与之对应的else
,否则电路中就容易出现锁存器
。
锁存器这种电路结构在非故意使用的情况下出现就是错误的,而else的不使用是造成锁存器被综合出来的原因之一。
修改:
reg [1:0]out;
always @(posedge clock)begin
if(s==2'b00)out<=2'b00;
else if(s==2'b11)out<=2'b11;
else out<=2'b11;
end
case中缺少default
在case语句中也容易出现锁存器
例:
reg [1:0] sel;
always @(sel,a,b)begin
case(sel)
2'b00:out = a + b;
2'b01:out = a - b;
2'b00:out = a + b;
2'b00:out = a + b;
end
该case语句中缺少了default,效果和if语句中缺少case一样,容易被综合工具综合成锁存器,无论default情况是否存在都要添加这一项,而且不要对其赋值为x,类似于:default: out= 2' bxx;
这样在仿真过程中是可以的,运行时比较类似电路刚启动所处的未知状态,但实际综合过程中x值是被忽略的,所以要给出一个明确的赋值。
如果设计者不知道应该在default中产生什么输出值,或者在else中产生什么输出值,也可以仅添加一个default而不添加任何语句,如下:default:;
组合与时序混合设计
栗子:
reg x, y, Z;
always @(x, y,z, posedge reset)begin //时序
if(reset)
out = 0; //时序
else
out = x ^ y ^ Z; //组合
end
一方面希望完成异或,另一方面又希望能在一个always结果中完成清零过程,得到一个混合设计模块。
从根本上将二者划分开是最好的解决途径:
reg x,y,Z;
always @(posedge reset)begin
if(reset)begin
x<=0;
y<=0;
z<=0;
end
else …
end
assign out =x^y^z;
在建立可综合模型时,能使用数据流语句实现组合逻辑电路时应尽量使用数据流描述建模,而不要使用行为级的阻塞赋值,因为assign语句层次较低,综合转化不容易发生歧义,所写语句与最后实现电路一致性较高。