关于时钟模块完备性验证方法第三章

系列文章目录


第三章 时钟占空比检查


文章目录

  • 系列文章目录

    文章目录

    前言

    一、什么是时钟的占空比?

    二、使用步骤

    1.自动化check代码代码如下:

    2. 代码解析

    总结


前言

之所以要进行时钟占空比检查,因为中后端对时钟建模的时候会要求时钟的最小脉冲宽度,当我们RTL产生时钟的时候,大部分情况会按照要求进行建模,但是也有可能出现时间产生的时钟与spec中定义的不一致,导致与中端generate的时钟不一致,影响最终的timing,另外对于复杂的时钟方案中,比如在时钟切换时,可能会在时钟切换时刻引入占空比不符合要求的情况,类似glitch这种,因此在验证时钟完备性时一定要进行占空比的检查。


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是时钟的占空比?

所谓时钟占空比,即我们常说的一个时钟周期内,高电平与低电平的比例,可以是1:1或者1:N,这个在spec中会标识出来,方便中端进行时钟建模,中端会根据此建模的时钟分析整个timing路径是否满足要求。所以,一旦中端建模的时钟与实际电路产生的时钟不吻合,很可能导致我们后仿出现timing违例,因此我们需要在EDA前仿真阶段将系统中所有与spec定义的不满足占空比的时钟找出来,保证实际代码与spec的一致性。

二、使用步骤

1.自动化check代码代码如下:

`define CLK_DUTY_CHK(chk_clk,high_ratio,low_ratio,jitter_margin,sign,report_err)
begin
  real t_last,t_delta,t_delta1,t_delat2,t_delat_abs;
  int cnt;
  t_last = 0.0;
  cnt = 0;
  fork
     begin
       while(1)begin
          @(posedge clk_ref);
          if(clk_check==0)begin
             cnt = 0;
             break;
          end
       end
     end
     begin
       while(1)begin
          @(chk_clk);
          if(cnt >= 1)begin
             t_delta = $realtime - t_last;
          end
          t_last = $realtime;
          cnt = cnt + 1;
          begin
             if(cnt[0]==1)begin
                t_delta1 = t_delta;
             end
             else begin
                t_delta2 = t_delta;
             end
          end
          begin 
             if(cnt > 2)begin
                if(high_ratio==low_ratio)begin
                   if(t_delta2 >= t_delta1)begin
                      t_delta_abs = t_delta2 - t_delat1;
                   end
                   else begin
                      t_delta_abs = t_detla1 - t_delta2;
                   end
                   if(t_delta_abs > jitter_margin)begin
                      $display(" clk duty check err", sign, $realtime);
                      err_num ++;
                   end
                end
             end
             else begin
                if(((t_delta2 > (0.997*((low_ratio/high_ratio)*t_delta1))) &&(t_delta2 < (1.002*((low_ratio/high_ratio)*t_delta1)))) || 
((t_delta1 > (0.997*((low_ratio/high_ratio)*t_delta2))) &&(t_delta1 < (1.002*((low_ratio/high_ratio)*t_delta2))))) begin
                end
                else begin
                    err_num ++;
                end
             end
          end
       end
     end
  join_any
  disable fork;
end

2. 代码解析

首先是这部分代码的主体结构,如下

`define CLK_DUTY_CHK(chk_clk,high_ratio,low_ratio,jitter_margin,sign,report_err)
begin
  real t_last,t_delta,t_delta1,t_delat2,t_delat_abs;
  int cnt;
  t_last = 0.0;
  cnt = 0;
  fork
     begin
      xxx;
     end
     begin
       xxx;
     end
  join_any
  disable fork;
end

这部分主体代码建立了一个fork join的并行执行语句,当两个begin end语句中任意一个执行完毕后退出检查。这样做的目的是为了更好的控制整个flow,第一个begin end语句中我们添加了使能信号,当clk_check为0时,退出检查,这样可以在更上层testbench中随时使能或关闭,第二个begin end为计算占空比的算法结构部分,通过计算的时钟占空比与目标时钟占空比进行比较,当误差范围超过设定的偏差值时,向testbench上报频率检查错误。

以下代码为第二个begin end算法结构:
 


       while(1)begin
          @(chk_clk);
          if(cnt >= 1)begin
             t_delta = $realtime - t_last;
          end
          t_last = $realtime;
          cnt = cnt + 1;
          begin
             if(cnt[0]==1)begin
                t_delta1 = t_delta;
             end
             else begin
                t_delta2 = t_delta;
             end
          end
          begin 
             if(cnt > 2)begin
                if(high_ratio==low_ratio)begin
                   if(t_delta2 >= t_delta1)begin
                      t_delta_abs = t_delta2 - t_delat1;
                   end
                   else begin
                      t_delta_abs = t_detla1 - t_delta2;
                   end
                   if(t_delta_abs > jitter_margin)begin
                      $display(" clk duty check err", sign, $realtime);
                      err_num ++;
                   end
                end
             end
             else begin
                if(((t_delta2 > (0.997*((low_ratio/high_ratio)*t_delta1))) &&(t_delta2 < (1.002*((low_ratio/high_ratio)*t_delta1)))) || 
((t_delta1 > (0.997*((low_ratio/high_ratio)*t_delta2))) &&(t_delta1 < (1.002*((low_ratio/high_ratio)*t_delta2))))) begin
                end
                else begin
                    err_num ++;
                end
             end
 

这部分算法的原理为,在每一个clk的变化沿,我们记录当前的时间,然后利用下一个时钟沿的时间减去前一个时钟沿的时间的方式,分别记录下高电平的时间以及低电平的时间,然后根据高电平与低电平的时间比例,与目标值进行比较,当不满足时则向testbench上报错误信息。

这里需要注意:由于占空比可能是1:1,也有可能是1:N,因此这里对齐进行了区分,同样这里也预留了一些margin,可以通过jitter_margin来进行设置,主要是为了消除仿真过程中timescale的影响。

`CLK_DUTY_CHK(clk_target,1.0,1.0.0.2,"clk_target")
 

在整个验证体统中通过调用上述宏定义来对所有待检查时钟进行实例化。如上是我们需要检查clk_target的时钟占空比高低为1:1,且预留margin为0.2ns。


总结

通过宏定义的方式对检查进行主体建模,然后在验证bench中对所有待检查的时钟进行扩展建模,很大程度上减少代码的复杂度,使代码通俗易懂,易维护。同时,通过这种自动化的时钟占空比检查,可以检查出任意时刻,当时钟占空比不满足设定的目标值时随时向testbench上报错误信息,大大提高验证效率。
 

你可能感兴趣的:(fpga开发,人工智能,开发语言,青少年编程,嵌入式硬件,单片机)