这篇是代码规范最后一篇,主要讲讲if语句和case语句在代码规范中的一些注意点。请酌情食用~
1. if语句优先级由上而下,越靠近下面的输入在综合时越靠近输出:(不允许if并列写法)
Example
always @(*)
begin
if(sel_A) Data_out = Data_A;
if(sel_B) Data_out = Data_B;
if(sel_C) Data_out = Data_C;
end
注:不推荐上述写法是因为没有else容易产生latch。同时,如果条件并非互斥,则可能会有意想不到的结果;
上述写法可以修改为如下可以避免latch:(Lint Latch Rules W18)
always @(*)
begin
Data_out = 1'b0;
if(sel_A) Data_out = Data_A;
if(sel_B) Data_out = Data_B;
if(sel_C) Data_out = Data_C;
end
2. if...else...if语句优先级由上向下,越靠近上面额度输入在综合时越靠近输出:(推荐写法)
Example
always @(*)
begin
if(sel_A)
Data_out = Data_A;
else if(sel_B)
Data_out = Data_B;
else
Data_out = Data_C;
end
3. 条件语句中不要放置组合逻辑
Example
Example 1:
if (A && (B | C)) #not recommended
Example 2:
assign D = A && (B | C); #recommended
if (D)
注:推荐的写法有利于进行验证波形分析;否则你得在工具中将这些组合逻辑使用公式产生对应的波形结果;
代码修改和阅读更方便;
总结:列全if...else...避免产生latch;条件语句不宜复杂;
1. 加default值可预防latch产生
(1) case的条件必须列全(full case),如果不能列全就写default值,否则会产生latch。相关SPYGLASS错误:
always @(*)
begin
case(select)
sel_A: D_out = A;
sel_B: D_out = B;
sel_C: D_out = C;
defalut: D_out = D;
endcase
end
(2) 如果case结构中的selector的位宽会随着design变化,那么即便是当前case条件是完备的,那么也可能变得不完备,因此需要加入default;
总结:强烈建议写代码时一定要用default语句!
2. case和if嵌套,也容易产生latch
(1) 不完整的if嵌套会产生latch
always @(*)
begin
case(valid)
2'b00: begin if(flag) valid_data = data[0]; end
2'b01: begin if(flag) valid_data = data[1]; end
2'b10: begin if(flag) valid_data = data[2]; end
2'b11: begin if(flag) valid_data = data[3]; end
defalut: valid_data = 1'b0;
endcase
end
(2) 写全if...else...可以去除latch
always @(*)
begin
case(valid)
2'b00: begin if(flag) valid_data = data[0];
else valid_data = 1'b0;
end
2'b01: begin if(flag) valid_data = data[1];
else valid_data = 1'b0;
end
2'b10: begin if(flag) valid_data = data[2];
else valid_data = 1'b0;
end
2'b11: begin if(flag) valid_data = data[3];
else valid_data = 1'b0;
end
defalut: valid_data = 1'b0;
endcase
end
(3) 在前面给一个初始赋值可以消除latch
always @(*)
begin
valid_data = 1'b0;
case(valid)
2'b00: begin if(flag) valid_data = data[0]; end
2'b01: begin if(flag) valid_data = data[1]; end
2'b10: begin if(flag) valid_data = data[2]; end
2'b11: begin if(flag) valid_data = data[3]; end
defalut: valid_data = 1'b0;
endcase
end
总结:case嵌套if...else...和if...else一样,不完整的if...else...会导致latch,注意列全或者在前面进行一次设置初始值;否则即便有default值也没有用;
3. 有多信号赋值时,需对所有条件下的所有信号进行描述。
(1) 如下有些case中缺少对某些输出的描述,会产生latch;
always @(*)
begin
valid_data = 1'b0;
case(valid)
2'b00: begin valid_data1 = data[0]; valid_data2 = data[0]; valid_data3 = data[0]; end
2'b01: begin valid_data1 = data[1]; valid_data3 = data[1]; end #没有data2的描述
2'b10: begin valid_data1 = data[2]; valid_data2 = data[2]; end #没有data3的描述
2'b11: begin valid_data1 = data[3]; end #没有data2和data3的描述
defalut: valid_data1 = 1'b0;
endcase
end
(2) 如果描述完全则不会产生latch;
always @(*)
begin
valid_data = 1'b0;
case(valid)
2'b00: begin valid_data1 = data[0]; valid_data2 = data[0]; valid_data3 = data[0]; end
2'b01: begin valid_data1 = data[1]; valid_data2 = data[1]; valid_data3 = data[1]; end
2'b10: begin valid_data1 = data[2]; valid_data2 = data[2]; valid_data3 = data[2]; end
2'b11: begin valid_data1 = data[3]; valid_data2 = data[3]; valid_data3 = data[3]; end
defalut: valid_data1 = 1'b0;
endcase
end
总结:在一个if...else语句中或case语句中,最好只对一个变量进行赋值,可以大大降低产生latch的风险;
4. case中只可以使用case和casez,不能使用casex
1. 组合逻辑的输出不可以用double flop进行同步。
原因就是组合逻辑的输出可能会有毛刺,这些毛刺会增大第一级flop产生metastable的概率,
进而影响整个synchronizer的MTBF,更严重的问题是由于第一级flop可能稳定在和输入adata不同的值,会导致bdata出现一个不该出现的值。
所以对于任何单bit信号,在跨时钟域之前一定要先寄存(flop),只有flop的输出才能经过下面的图就是不flop的情形。
2. 不依赖Verilog默认的优先级,每个运算都加括号。