Verilog代码优化技巧:
1. 条件b为TRUE时,将c赋值给a;
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b)
a <= c;
else
a <= a;
🎿时序逻辑里,后面的else如果不写的话,综合时会自动插入门控时钟。也就是说,只有满足if和else if里的条件,时钟fclk才有效,否则fclk无效,寄存器不翻转,从而达到节省功耗的目的;
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b)
a <= c;
2. 条件b为TRUE时, 若c为TURE将d赋值给a,否则将a赋值给a。
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b)
a <= c ? d : a;
👾其实b, c均为TRUE时,a才翻转,因此完全可以写成
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b & c)
a <= d;
3. b为true时,将a1+a2赋值给a,否则若c为true,则将a3+a4赋值给a;
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b)
a <= a1 + a2;
else if(c)
a <= a3 + a4;
💎直接这样写会综合出两个加法器,但实际上b和c两个分支不可能同时运行,因此只需要一个加法器即可:
assign a_tmp0 = b ? a1 : a3;
assign a_tmp1 = b ? a2 : a4;
assign a_tmp = a_tmp0 + a_tmp1;
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b | c)
a <= a_tmp;
4. 设存在2bit寄存器cnt, 和4bit寄存器a, 当输入b_vld为1时,将1bit数据b写进cnt对应的a的位置; 例如cnt为2‘b11, 则a[3] <= b;
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b_vld)
case(cnt)
'd0: a[0] <= b;
'd1: a[1] <= b;
'd2: a[2] <= b;;
default : a[3] <= b;
endcase
🏆这样写有个缺点是,如果cnt和a的位宽很大,那case就会写的非常的长;每次只会更新cnt对应的位宽,那我们可以设一个wire变量a_tmp,当cnt==i时,将对应的a_tmp[i]赋值为b,否则和a[i]一样; b_vld有效时,将a_tmp赋值给a;
genvar i;
generate
for (i = 0 ; i < 4 ; i++)
begin: a_value
assign a_tmp[i] = cnt==i ? b ? a[i];
end
endgenerate
always@(posedge fclk or negedge frstn)
if(!frstn)
a <= 0;
else if(b_vld)
a <= a_tmp;
5. 多个1bit信号的翻转
always@(posedge fclk or negedge frstn)
if(!frstn)
a_1d <= 0;
else if(vld)
a_1d <= a;
always@(posedge fclk or negedge frstn)
if(!frstn)
b_1d <= 0;
else if(vld)
b_1d <= b;
always@(posedge fclk or negedge frstn)
if(!frstn)
c_1d <= 0;
else if(vld)
c_1d <= c;
always@(posedge fclk or negedge frstn)
if(!frstn)
d_1d <= 0;
else if(vld)
d_1d <= d;
🎯一般来说,4bit以下的寄存器,即使没有以else结尾,综合工具也不会自动插入门控时钟。因为插入门控时钟需要额外的电路,为了4bit以下的寄存器做门控时钟,节省的功耗和需要增加的,面积相比,不划算。
always@(posedge fclk or negedge frstn)
if(!frstn)
begin
a_1d <= 0;
b_1d <= 0;
c_1d <= 0;
d_1d <= 0;
end
else if(vld)
begin
a_1d <= a;
b_1d <= b;
c_1d <= c;
d_1d <= d;
end
🎮如上段代码所示,这样的话,综合工具就会将a_1d,b_1d,c_1d和d_1d,看成一个大的寄存器去处理,就会插入门控时钟了;但需要注意的是,a_1d,b_1d,c_1d和d_1d,的翻转条件必须是一样的,否则不会看成一个整体去插入时钟,比如:
always@(posedge fclk or negedge frstn)
if(!frstn)
begin
a_1d <= 0;
b_1d <= 0;
c_1d <= 0;
d_1d <= 0;
end
else if(vld0)
begin
a_1d <= a;
b_1d <= b;
c_1d <= c;
d_1d <=d;
end
else if(vld1)
begin
a_1d <= a;
end
🌂a_1d的翻转条件是vld0 | vld1, 而其他寄存器是vld0,翻转条件不一样,综合时会将b_1d,c_1d和d_1d看成一个寄存器,将a_1d作为一个单独寄存器,去进行综合优化。
6. 总结
verilog设计里,主要思路就是两点:
- 🎩1. 寄存器能不翻转就不翻转,寄存器的时钟能关掉就关掉:减小翻转功耗;
- 👑2. 运算单元和寄存器,能复用就复用:减小芯片面积;