这一节简单聊聊 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