芯片前端工程中,测试验证的核心理念:以提高覆盖率为核心。
设计工程师需要关心的主要有行覆盖率(Block),条件覆盖率(Expression),翻转覆盖率(Toggle),状态机覆盖率。本文从ASIC设计的角度上来讨论,如何写出高覆盖率的Verilog代码。
按位运算逻辑,& | ^ ^~和三目运算符,慎用。
使用这样的描述方式本身功能并没有什么问题,而且写起来很爽,但是在很多情况下覆盖率是真的不好收。
assign mult_a[3:0] = ({4{mult0_vld}} & mult_a0)
| ({4{mult1_vld}} & mult_a1)
| ({4{mult2_vld}} & mult_a2)
用或门和与门实现的一个选择器的功能,前提是vld不能同时有效,相对于下面第二种写法可能会节省一点门。
但是问题我们在收集Expression时需要分析每一个条件是否跑到0/1,上面一共有六个信号,所以0、1随机组合的情况就有2的6次方种,mult_a0作为数据端,如果没有出现过全0的情况,通过定向case可以覆盖到,但如果是参数作为选择器的输入端,那么参数本身就是有永远不为0的情况,定向case也无法通过。
所以这个时候就只能把它waive(放弃)/exclude(排除)掉,并解释原因。如果只有几条这样的写法还好,如果有成百上千条,那么就需要重复上面的操作上千次。单纯的体力活,没有任何技术含量。但是直接换一种写法。
always @(*)begin
if(mult0_vld)
mult_a[3:0] = mult_a0;
else if(mult1_vld)
mult_a[3:0] = mult_a1;
else // if(mult2_vld)
mult_a[3:0] = mult_a2;
end
always @(*)begin
if(data_vld && mode_sel && enable_flag && (data_num[3:0] > 4'd7) && (ram_addr[4:0] > 4'd15) && ...)
end
else ...
assign enable = ((cur_state != STATE_A) && (next_state == STATE_A))
|| ((cur_state != STATE_B) && (next_state == STATE_B))
always @(*)begin
case(in[1:0])
2'd0 : data[1:0] = 2'd0;
2'd1 : data[1:0] = 2'd1;
2'd2 : data[1:0] = 2'd2;
default : data[1:0] = 2'd3;
endcase
end
always @(*)begin
if(start)
cnt <= 'd0;
else if(((para == 3) && (cnt != 3))
|| ((para == 4) && (cnt != 7))
|| ((para == 5) && (cnt != 15))
|| ((para == 6) && (cnt != 31)))
cnt <= cnt + 1'd1;
...
end
assign data_vld = (param == 0) || (param == 1) || (param == 2);