当前位置 博文首页 > zhoujianjayj的博客:Verilog代码规范(五) -- if & case语

    zhoujianjayj的博客:Verilog代码规范(五) -- if & case语

    作者:[db:作者] 时间:2021-08-30 22:25

    代码规范(五)

    这篇是代码规范最后一篇,主要讲讲if语句和case语句在代码规范中的一些注意点。请酌情食用~


    一、if语句

    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;条件语句不宜复杂;

    ?

    二、case语句

    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默认的优先级,每个运算都加括号。

    ?

    ?

    cs