Verilog代码规范(三) -- assign & always & for

这一节简单聊聊 assign & always & for 三种语句中会出现的代码规范问题。


代码规范(三)

一、assign语句

1. 数值的实际位宽大于指定的位宽会导致截位。如果截掉的bit中非全零,可能会错;(Spyglass Assign Rules W19)

- 2'b111: 发生截位,会报W19;
- 2'b011:默认不会报,但是若设置了strict,则会报W19
- 2'd15, 1’d3:发生截位,会报W19;
- 2'd03, 4'd0014:虽然发生截位,但是不报W19,因为截断位都是0;
- 3'hF, 2'o7:会报错
- 3'h1, 2'o1:虽截位但是不报错;

2. assign赋值两边的位宽不匹配(Spyglass Rule RHS>LHS, W164a)(Spyglass Rule RHS<LHS, W164b)

对于W164a,会导致溢出、截位,造成数据丢失:

//Example 1
wire a,b;
assign a = b + 4 ; //Violation reported with strict

//Example 2
A [11:0] = { 3’b000, b[9:0] } ; //no violation,leading zeros can ignore;

对于W164b,允许出现,综合会自动补零,但是不建议出现。

位宽的不匹配主要是出现在运算后,这部分在这里不赘述;

3. 除了testbench,不要在赋值中使用delay,因为不可综合;(Spyglass Assign Rules W280, W257)

a <= #10 b;

4. 时序逻辑块中使用非阻塞赋值,组合逻辑块中使用阻塞赋值;(Assign Rules W336)

module fbosc1(y1,y2,clk,rst);
	output y1, y2;
	input  clk, rst;
	reg    y1, y2;

	always @(posedge clk pr posedge rst)
		if(rst) y1=0;
		else y1=y2;

	always @(posedge clk or posedge rst)
		if(rst) y2=1;
		else y2=y1;

endmodule

理论上,两个always块是并行执行的。若复位信号从1到0解复位:

若第一个always块的有效时钟沿比第二个always块的时钟沿几个皮秒(时钟树误差)到达,则y1和y2都会取1;

若第一个always块的有效时钟沿比第二个always块的时钟沿几个皮秒(时钟树误差)到达,则y1和y2都会取0;

这说明,这个模块会产生冒险和竞争。同时也不能保证综合处争取的逻辑,是的前仿和后仿产生不同的结果;

注意两点:

1)在描述组合逻辑的always块中用阻塞赋值,则综合成组合逻辑的电路结构;

(2)在描述时序逻辑的always块中用非阻塞赋值,则综合成时序逻辑的电路结构;

 

二、always语句

1. 一个always语句内不使用多个时钟边沿。换句话说,一个always只能有一个时钟;Example:

always begin            
	@(posedge CLK1)       
		QA <= A;
	@(posedge CLK2)
		QA <= B;
end

2. 一个always块内,只对一个变量赋值,或者对非常相关的一类信号赋值,避免不相关的信号同时赋值。Example:

always @(*)                     //多个信号在一个always块中,不提倡
if(A==1 && B==0) begin
	DOUT = 1;
	EOUT = ZIN;
	FOUT = 0;
	GOUT = 0;
end
else if(A==0 && B==0) begin
	DOUT = 0;
	EOUT = YIN;
	FOUT = 0;
	GOUT = 0;
end
else if(A==0 && B==0) begin
	DOUT = 0;
	EOUT = YIN;
	FOUT = 1;
	GOUT = 0;
end
else if(C==1) begin
	DOUT = 1;
	EOUT = ZIN;
	FOUT = 1;
	GOUT = 1;
end
else begin
	DOUT = 1;
	EOUT = ZIN;
	FOUT = 1;
	GOUT = 0;
end
always @(*)                     //拆分成多个always块,不容易产生latch
if(A==1) begin
	DOUT = 1;
	EOUT = ZIN;
end
else begin
	DOUT = 0;
	EOUT = YIN;
end

always @(*) begin                   //
	if(B==1) 
		FOUT = 1;
	else 
		FOUT = 0;
end

always @(*) begin                   //
	if(A==1 && B==1 && C==1) 
		GOUT = 1;
	else 
		GOUT = 0;
end

3. 使用组合逻辑时,统一使用“always@(*)”的形式。防止括号里面的防止敏感信号列不全;Exapmle:

always @(A, B, C) //很容易列漏掉敏感信号

always @(*)       //有效避免漏列敏感信号

4. 模块内禁止同一信号在不同的always逻辑块中赋值;(Spyglass Assign Rules W505)Example:

always @(posedge clk or negedge res_n)
begin
	if(!res_n)
		q <= 4'h0
	else if (b_en) begin
		q <= q << 1;                  //
	end
end

//
always @(posedge clk or negedge res_n)
begin
	if(!res_n)
		q <= 4'h0
	else if (a_en) begin
		q[0] <= si;
	end
end

5. 禁止在时序电路中对同一信号重复赋值;(MultipleDriver Rules W415)Example:

always @(posedge clk or negedge res_n)
begin
	if(!res_n)
		q <= 4'h0
	else if (en) begin
		q <= q << 1;                  //对同一信号赋值不可取
		q[0] <= si;
	end
end

//
always @(posedge clk or negedge res_n)
begin
	if(!res_n)
		q <= 4'h0
	else if (en) begin
		q <= {q[2:0],si};                  //
	end
end

 

6. 组合逻辑和时序逻辑不可混杂使用。时序逻辑就是流水,组合逻辑就是每个流水阶段要做的事情。不要混在一起;​​​​​​​

7. 信号被always组合逻辑块使用,但是没有在敏感列表中定义。使用*而非列表可以避免此问题。​​​​​​​

8. 信号处于敏感列表,但不是所有的bit都被用到。使用*而非列表可以避免此问题。

9. 确保敏感列表里面的信号在always中没有被修改。使用*而非列表可以避免此问题。

10. 敏感列表中想表达or,却出现了|或者||。使用*而非列表可以避免此问题。

不难看出,对于组合逻辑的always块中 的敏感列表,写*而非列表可以避免很多不必要的错误,而这些error/warning在spyglass检查中,也都会有所体现。​​​​​​​

 

三、for语句

1. for循环语句可能被终止,但是综合不会,综合会把for循环全部条件下的电路翻译出来;

reg  [3:0] i;
wire [2:0] tmp;
assign tmp = A-1;

always @(*) begin
	for (i=0; i<8;I=i+1)
		if(i > tmp)
			s[i] = 1'b1;
		else
			s[i] = 1'b0
end

2. 如果需要for语句,for语句可以转换成case语句来实现

wire [2:0] tmp;
assign tmp = A-1;

always @(*) begin
	case(tmp)
		3'b000: s=8'b0000_0000;
		3'b001: s=8'b1111_1110;
		3'b010: s=8'b1111_1100;
		3'b011: s=8'b1111_1000;
		3'b100: s=8'b1111_0000;
		3'b101: s=8'b1110_0000;
		3'b110: s=8'b1100_0000;
		3'b111: s=8'b1000_0000;
		default: s=8'b0000_0000;
	endcase
end

3. 在Verilog中除了在Testbench(仿真测试激励)中使用for循环语句外,但在RTL级编码中却很少使用for循环语句。

主要原因:for循环会被综合器展开为所有变量情况的执行语句,每个变量独立占用寄存器资源,每条执行语句并不能有效地复用硬件逻辑资源,造成巨大的资源浪费。

简单的说就是:for语句循环几次,就是将相同的电路复制几次,因此循环次数越多,占用面积越大,综合就越慢。

在RTL硬件描述中,遇到类似的算法,推荐的方法是先搞清楚设计的时序要求,做一个reg型计数器。

在每个时钟沿累加,并在每个时钟沿判断计数器情况,做相应的处理,能复用的处理模块尽量复用,即使所有的操作不能复用,也采用case语句展开处理。

for(i=0;i<16;i++)
  DoSomething();
reg [3:0] counter;
always @(posedge clk)
  if(syn_rst)
    counter<=4'b0;
  else
    counter<=counter+1;
always @(posedge clk)
  begin
    case(counter)
        4'b0000:
        4'b0001:
        ......
    default:
    endcase
  end

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值