Verilog代码规范(五) -- if & case语句

代码规范(五)

这篇是代码规范最后一篇,主要讲讲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默认的优先级,每个运算都加括号。

 

 

你可能感兴趣的:(Verilog代码规范,verilog,代码规范)