一、背景
在验证环境中已经有了一个不带缺口的时钟,现在需要将这个不带缺口的时钟每隔10拍扣去1拍,然后在这个带缺口的时钟下发送激励给DUT。
二、错误示例
int cnt_10;
logic vld;
logic gap_clk;
initial begin
cnt_10 = 0;
forever begin
@(posedge clk);
cnt_10++;
cnt_10 = cnt_10 % 10;
if(cnt_10 == 0) vld = 0;
else vld = 1;
end
end
assign gap_clk = vld & clk;
把波形dump毛刺的选项(+fsdb+delta)打开后,可以看到这种方式生成的gap_clk是带有毛刺:
如果我们使用该时钟来发送激励,那么在有毛刺的瞬间,仿真器会认为存在gap_clk的跳变,激励也就不满足要求了。
假设激励设置如下:
logic [31:0] drv_data;
always @(posedge gap_clk) begin
drv_data <= $urandom();
end
仿真得到的激励:
三、毛刺的分析
int cnt_10;
logic vld;
logic gap_clk;
initial begin // process1
cnt_10 = 0;
forever begin
@(posedge clk);
cnt_10++;
cnt_10 = cnt_10 % 10;
if(cnt_10 == 0) vld = 0;
else vld = 1;
end
end
assign gap_clk = vld & clk; // process2
基于systemverilog的仿真调度机制来分析。假定当前的cnt_10=9,此时clk发生0->1的跳变,clk在跳变后为1,此时会触发process1和process2。process1和process2的计算会得到vld=0和gap_clk=1。因为此时所有的process已经计算完成,所以vld=0和gap_clk=1会被保存。接着由于vld的值发生1->0的跳变,会再一次地触发process2的计算,此时process2的计算会得到gap_clk=0,该结果也会被保存。由于上述的计算都发生在一个时刻点上,所以gap_clk就会呈现出毛刺,该毛刺进一步导致仿真结果不符合预期。
四、正确示例
实现可以参考stdcell中ICG的行为描述,利用负沿来锁存vld。ICG的行为描述如下:
reg E_LAT;
always @(*) begin
if(CP == 1'b0)
E_LAT = E;
else
E_LAT = E_LAT;
end
assign Q = CP & E_LAT;
因此只需要将代码中的posedge改成negedge即可,修改后效果如下:
其实这种写法还是偏向于设计的写法。如果是偏向于验证的写法,最好还是不要出现assign语句,直接在一个block中进行行为描述。