在上一篇文章里,通过DC综合的网表我们再次明确了一下信号截位与补全行为的参考准则:
最后的结论就是:不用担心,过程中的位宽截断会按最终所需的位宽进行充分保留。而后我们回到现实,如果项目组就是要求我们尽可能清理全部的lint error和warning,那么对于运算的位宽问题应该如何处置呢?在此强调一下,我个人认为一些error和warning是无需处理甚至不可避免的,所以这里只是讨论在“尽可能清理”要求下的最优处理方式,同时讨论的前提是我们明确最终的输出位宽确实就是后续所需要的大小不存在溢出的风险。
众所周知(瞎说的),在verilog RTL信号运算表达式里,存在一个不可能关系:
我们以下面这段代码为例,目的是要计算c = a + b:
module add_test #(
inb, outc);
input [8 -1:0]ina;
input [8 -1:0]inb;
output [8 -1:0]outc;
assign outc = ina + inb;
endmodule
如果以这种方式通过spyglass进行lint检查,会报什么错误呢?没错,会报经典的Error W484:
wire [9 -1:0]outc_ex = ina + inb;
assign outc = outc_ex[8 -1:0];
wire [9 -1:0]outc_ex = {1'b0, ina} + {1'b0, inb};
assign outc = outc_ex[8-1:0];
之后就只剩下一条warning了:
也就是说想彻底消除加法(以及减法和乘法)里的“进位拓展”和“位宽匹配”矛盾,需要先把两个加数高位补0至进位后的位宽,同时加和的结果要声名为进位后的位宽。举几个另外的例子,比如三个8bit的数相加写成这样就不会触发W484和W164b:
input [8 -1:0]ina;
input [8 -1:0]inb;
input [8 -1:0]ind;
output [8 -1:0]outc;
wire [10 -1:0]outc_ex = {2'b0, ina} + {2'b0, inb} + {2'b0, ind};
assign outc = outc_ex[8-1:0];
input [8 -1:0]ina;
input [1 -1:0]inb;
output [8 -1:0]outc;
wire [9 -1:0]outc_ex = {1'b0, ina} + {8'b0, inb};
assign outc = outc_ex[8-1:0];
input [8 -1:0]ina;
input [7 -1:0]inb;
output [8 -1:0]outc;
wire [15 -1:0]outc_ex = {7'b0, ina} * {8'b0, inb};
assign outc = outc_ex[8-1:0];
Cells do not drive (LINT-1)
input [8 -1:0]ina;
input [8 -1:0]inb;
output [8 -1:0]outc;
wire [9 -1:0]outc_ex = {1'b0, ina} + {1'b0, inb};// spyglass disable W498
assign outc = outc_ex[8-1:0];
module cal_test #(
parameter INA_W = 8,
parameter INB_W = 8,
parameter OUT_W = 16,
parameter CAL_M = 3 //0 for add, 1 for sub, 2 for mul, 3 for div
)(ina, inb, outc
);
input [INA_W -1:0]ina;
input [INB_W -1:0]inb;
output [OUT_W -1:0]outc;
localparam OUT_WX_W = (((CAL_M == 0) || (CAL_M == 1)) && (INA_W >= INB_W)) ? (INA_W + 1) :
(((CAL_M == 0) || (CAL_M == 1)) && (INA_W < INB_W)) ? (INB_W + 1) :
(CAL_M == 2) ? (INA_W + INB_W) : INA_W ;
wire [OUT_WX_W -1:0]outc_ex;// spyglass disable W498
generate
if(CAL_M == 0)begin: CAL_M_ADD
assign outc_ex = {{(OUT_WX_W-INA_W){1'b0}}, ina} + {{(OUT_WX_W-INB_W){1'b0}}, inb};
end
else if(CAL_M == 1)begin: CAL_M_SUB
assign outc_ex = {{(OUT_WX_W-INA_W){1'b0}}, ina} - {{(OUT_WX_W-INB_W){1'b0}}, inb};
end
else if(CAL_M == 2)begin: CAL_M_MUL
assign outc_ex = {{(OUT_WX_W-INA_W){1'b0}}, ina} * {{(OUT_WX_W-INB_W){1'b0}}, inb};
end
else begin: CAL_M_DIV
assign outc_ex = {{(OUT_WX_W-INA_W){1'b0}}, ina} / {{(OUT_WX_W-INB_W){1'b0}}, inb};
end
endgenerate
generate
if(OUT_WX_W >= OUT_W)begin: OUT_WX_W_BIG
assign outc = outc_ex[OUT_W -1:0];
end
else begin: OUT_W_BIG
assign outc = {{(OUT_W-OUT_WX_W){1'b0}}, outc_ex};
end
endgenerate
endmodule
系列文章入口——
【芯片验证】sva_assertion: 15道助力飞升的断言练习 |
【芯片验证】可能是RTL定向验证的巅峰之作 |
【芯片验证】RTL仿真中X态行为的传播 —— 从xprop说起 |
【芯片验证】年轻人的第一个systemVerilog验证环境全工程与解析 |
【芯片设计】verilog中有符号数和无符号数的本质探究 |
【芯片设计】论RTL中always语法的消失术 |
【芯片设计】代码即注释,注释即代码 |
【芯片设计】700行代码的risc处理器你确实不能要求太多了 |
入职芯片开发部门后,每天摸鱼之外的时间我们要做些什么呢 |
如何计算系统的outstanding 和 burst length? |
芯片搬砖日常·逼死强迫症的关键词不对齐事件 |
熟人社会里,一群没有社会价值的局外人 |