在上一篇文章里,通过DC综合的网表我们再次明确了一下信号截位与补全行为的参考准则:
【芯片设计】综合中信号截位与补全的策略测试
最后的结论就是:不用担心,过程中的位宽截断会按最终所需的位宽进行充分保留。而后我们回到现实,如果项目组就是要求我们尽可能清理全部的lint error和warning,那么对于运算的位宽问题应该如何处置呢?在此强调一下,我个人认为一些error和warning是无需处理甚至不可避免的,所以这里只是讨论在“尽可能清理”要求下的最优处理方式,同时讨论的前提是我们明确最终的输出位宽确实就是后续所需要的大小不存在溢出的风险。
众所周知(瞎说的),在verilog RTL信号运算表达式里,存在一个不可能关系:
我们以下面这段代码为例,目的是要计算c = a + b:
module add_test #()(ina, 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:
好的尽管我们知道我们需要的就是8bit的输出,为了消除这个error那就把左边比右边宽1bit吧:wire [9 -1:0]outc_ex = ina + inb;assign outc = outc_ex[8 -1:0];而后就会发现,一个error兑换成了两个warning:
W164b这个warning仿佛就是要跟W484对着干的,一样宽不行左边拓展位宽了还不行,看起来怎么也避免不了倒向某一侧。不过实际上W164b是可以消除的,此时需要把代码改成这样: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];又或是一个8bit的ina和1bit的inb相加,写成这样就可以: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];这对矛盾解决了,那么剩下的就是W498高位未被使用了。说真的,这个我目前暂时没有看到什么办法能消除掉,(所以这是不是前司放弃了spyglass而使用nLint和VCST的原因,或许其他工具能够更智能的识别出这种场景?)但是这个warning在我迄今为止的项目交付里都是可以被忽略的,毕竟有信号没有被读取就跟综合里的LINT-1一样是没有什么隐患的(不像LINT-2 not load发现就打死):Cells do not drive (LINT-1)所以要么在总的规则里把W498屏蔽,要么手动waive,或者直接在RTL中注释:
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 W498assign outc = outc_ex[8-1:0];这样呢,不可能三角才能完全消除掉。所以绕了这么一大圈,如果真的遭遇了项目组要求“必须、尽力、尽可能、只要结果不要过程”的清理lint error和warning的情况,我觉得要不就是一点点改,或者去搞个cbb出来把普通的加减包一下变成这样: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 1) : (CAL_M == 2) ? (INA_W + INB_W) : INA_W ;
wire [OUT_WX_W -1:0]outc_ex;// spyglass disable W498generate 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}; endendgenerate
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}; endendgenerate
endmodule之后通过调用cbb来完成普通的四则运算,这样无论加减乘除(当然一般也不会直接做除法)就都不会报error和warning,确实不失为一种快速满足项目组要求的手段。但是我仍然觉得是有些多此一举的ε=(′ο`*)))。
系列文章入口——
【芯片设计】SoC 101(一):绪论 | 【芯片设计】FIFO漫谈(零)从无处不在的FIFO开始说起 | 【芯片设计】计算机体系结构(一)虚拟内存 | 【芯片设计】深入理解AMBA总线(零)绪论
| 【芯片设计】握手协议的介绍与时序说明 | 【芯片设计】复位那些小事 —— 复位消抖 | 【芯片设计】快速入门数字芯片设计(一)Introduction | 【芯片验证】UVM源码计划(零)下定决心读源码前的自测环节
| 【芯片设计】异步电路碎碎念(一) 到底什么是异步电路
| 【芯片设计】从RTL到GDS(一):Introduction
| 其他文章链接——
【芯片验证】sva_assertion: 15道助力飞升的断言练习 | 【芯片验证】可能是RTL定向验证的巅峰之作 | 【芯片验证】RTL仿真中X态行为的传播 —— 从xprop说起 | 【芯片验证】年轻人的第一个systemVerilog验证环境全工程与解析 |
【芯片设计】verilog中有符号数和无符号数的本质探究
| 【芯片设计】论RTL中always语法的消失术 | 【芯片设计】代码即注释,注释即代码 | 【芯片设计】700行代码的risc处理器你确实不能要求太多了 |
入职芯片开发部门后,每天摸鱼之外的时间我们要做些什么呢 | 如何计算系统的outstanding 和 burst length? | 芯片搬砖日常·逼死强迫症的关键词不对齐事件 | 熟人社会里,一群没有社会价值的局外人 |
|