core-v-verif系列之lib<10>

UVM环境介绍
HEAD commitID: 1f968ef

1. core-v-verif/lib/cv_dv_utils/uvm/clock_mon/clock_monitor_config_c.svh


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  Description : Package with defines for XRTL portion of clock IF
//                
// 
// ----------------------------------------------------------------------------

package clock_mon_vif_xrtl_pkg;

  class xrtl_clock_mon_vif_c;

    // ----------------------------------------------------------------------
    // Name
    // ----------------------------------------------------------------------

    protected string name;

    // ----------------------------------------------------------------------
    // Constructor
    // ----------------------------------------------------------------------

    function new( string name_in ) ;
        name = name_in;
    endfunction : new

  endclass : xrtl_clock_mon_vif_c

endpackage : clock_mon_vif_xrtl_pkg

1. 简要介绍
package clock_mon_vif_xrtl_pkg;

  class xrtl_clock_mon_vif_c;

    // ... 类成员和方法 ...

  endclass : xrtl_clock_mon_vif_c

endpackage : clock_mon_vif_xrtl_pkg

代码介绍
此代码定义了一个名为 clock_mon_vif_xrtl_pkg 的 SystemVerilog 包,包内包含一个类 xrtl_clock_mon_vif_c。该包主要用于封装与 XRTL 部分时钟接口监控相关的类,可能在验证环境里用于监控时钟接口的行为。

2. 接口介绍
2.1 构造函数
function new( string name_in ) ;
    name = name_in;
endfunction : new

代码分析

  • function new( string name_in ) ;:定义了 xrtl_clock_mon_vif_c 类的构造函数,它接收一个字符串类型的参数 name_in,用于初始化对象的名称。
  • name = name_in;:将传入的参数 name_in 赋值给类的成员变量 name,完成对象名称的初始化。
3. 参数介绍
protected string name;

代码分析

  • protected string name;:声明了一个受保护的字符串类型成员变量 name。受保护意味着该变量只能在类内部以及其派生类中访问,用于存储该类对象的名称。
4. 模块实现介绍
4.1 包的定义
package clock_mon_vif_xrtl_pkg;
// ...
endpackage : clock_mon_vif_xrtl_pkg

代码分析

  • package clock_mon_vif_xrtl_pkg;:开始定义一个名为 clock_mon_vif_xrtl_pkg 的包,包是 SystemVerilog 中用于组织代码的一种机制,可以将相关的类型、类、常量等封装在一起。
  • endpackage : clock_mon_vif_xrtl_pkg;:结束包的定义。
4.2 类的定义
class xrtl_clock_mon_vif_c;
    // ...
endclass : xrtl_clock_mon_vif_c

代码分析

  • class xrtl_clock_mon_vif_c;:开始定义一个名为 xrtl_clock_mon_vif_c 的类,类是面向对象编程的基本单元,可包含数据成员和方法。
  • endclass : xrtl_clock_mon_vif_c;:结束类的定义。
5. 总结

clock_mon_vif_xrtl_pkg.sv 文件定义了一个包 clock_mon_vif_xrtl_pkg,其中包含类 xrtl_clock_mon_vif_c。该类目前仅包含一个用于存储对象名称的受保护成员变量和一个简单的构造函数。整体来看,这个类的功能比较基础,可能是一个框架性的类,后续可以在类中添加更多的方法和成员变量,以实现对 XRTL 部分时钟接口的监控功能,例如添加时钟信号的采样、分析等方法。

2. core-v-verif/lib/cv_dv_utils/uvm/clock_mon/clock_monitor_c.svh


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  Description : Monitor for clock signal(s)
//
//
//
// ----------------------------------------------------------------------------

class clock_monitor_c extends uvm_monitor;

    `uvm_component_utils( clock_monitor_c )

    // ------------------------------------------------------------------------
    // Virtual interfaces - obtained from config_db
    // ------------------------------------------------------------------------

    virtual xrtl_clock_mon_vif m_v_clock_mon_vif;

    // ------------------------------------------------------------------------
    // Variable declaration
    // ------------------------------------------------------------------------

    bit m_clk_mon_en;


    // ------------------------------------------------------------------------
    // Local Parameter
    // ------------------------------------------------------------------------

    localparam int NUMBER_OF_CLK_STABLE = 50;

    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------

    function new( string name, uvm_component parent );
      super.new(name, parent);
    endfunction: new

    // ------------------------------------------------------------------------
    // Build phase
    //
    // Get the virtual interface from the configuration DB.
    // ------------------------------------------------------------------------

    function void build_phase( uvm_phase phase );

      super.build_phase( phase );

        // Obtain the Virtual interface from the configuration DB
        if ( !uvm_config_db #( virtual xrtl_clock_mon_vif)::get(this, "",  get_name(), m_v_clock_mon_vif ))
          `uvm_error("BUILD_PHASE", {"UNABLE to get Virtual Interface for [get_full_name]= ", get_full_name(), " from the configuration database" } )

    endfunction: build_phase


    // ------------------------------------------------------------------------
    // Function: enable_auto_clk_monitor
    // ------------------------------------------------------------------------
    function void enable_auto_clk_monitor(int min_freq_Hz, int max_freq_Hz);

      // Period in picosecond + 0.5 Round not truncate
      m_v_clock_mon_vif.clk_min_period_ps = ((1.0/max_freq_Hz) * 1e12) + 0.5;
      m_v_clock_mon_vif.clk_max_period_ps = ((1.0/min_freq_Hz) * 1e12) + 0.5;

      if (min_freq_Hz > max_freq_Hz)
        `uvm_warning( "CLOCK MONITOR", "WARNING: MIN frequency > MAX frequency" );

      m_v_clock_mon_vif.clock_mon_enable();

      `uvm_info( "CLOCK MONITOR", $sformatf("Enabling clock monitor: %s with MIN frequency = %0d (Hz) and MAX frequency = %0d (Hz) - MIN period=%0d (ps) and MAX period=%0d (ps)", get_name(),
         min_freq_Hz, max_freq_Hz, m_v_clock_mon_vif.clk_min_period_ps, m_v_clock_mon_vif.clk_max_period_ps ), UVM_MEDIUM )

    endfunction : enable_auto_clk_monitor


    // ------------------------------------------------------------------------
    // Function: disable_clk_monitor
    // ------------------------------------------------------------------------
    function void disable_clk_monitor();

       m_v_clock_mon_vif.clock_mon_disable();
      `uvm_info( "CLOCK MONITOR", $sformatf("Disabling clock monitor: %s", get_name() ), UVM_MEDIUM )

    endfunction : disable_clk_monitor

    // ------------------------------------------------------------------------
    // Function: clk_is_stable
    // ------------------------------------------------------------------------

    function int clk_is_stable();

      if ( m_v_clock_mon_vif.num_consecutive_clk_stable >= NUMBER_OF_CLK_STABLE )
        begin
          `uvm_info( "CLOCK MONITOR", $sformatf("Clock is Stable"), UVM_MEDIUM )
          return 1;
        end
      else
        return 0;

    endfunction : clk_is_stable


    // ------------------------------------------------------------------------
    // Function: is_clk_flat
    // ------------------------------------------------------------------------

    function logic is_clk_flat( longint flat_duration_in_ps );
       longint time_last_edge;
       longint curr_time;
       longint time_limit;

       curr_time = m_v_clock_mon_vif.get_current_time_ps() ;
       time_last_edge  = m_v_clock_mon_vif.get_time_last_edge_ps() ;
       time_limit = time_last_edge + flat_duration_in_ps;

       `uvm_info( "CLOCK MONITOR", $sformatf("is_clk_flat : %0d %0d %0d %0d",
                          curr_time, time_last_edge, time_limit,
                          ( time_limit < curr_time  ) ), UVM_DEBUG );

       return ( time_limit <  curr_time ) ;
    endfunction : is_clk_flat

    // ------------------------------------------------------------------------
    // Function: get_clk_freq_Hz
    // ------------------------------------------------------------------------
    function longint get_clk_freq_Hz();

      real m_mon_clk_Hz;

      if (m_v_clock_mon_vif.current_period == 0)
        `uvm_fatal("CLOCK MONITOR", "Clock period = 0 ... Did you wait long enough to get a proper measurement?" )

      m_mon_clk_Hz = (1.0/(m_v_clock_mon_vif.current_period * 1e-12));
      `uvm_info("CLOCK MONITOR", $sformatf("[get_clk_freq_Hz] period=%0d (ns) - frequency=%5.0f (Hz) @ time=%0t", m_v_clock_mon_vif.current_period, m_mon_clk_Hz, $time ), UVM_DEBUG )
      return m_mon_clk_Hz;

    endfunction : get_clk_freq_Hz



endclass : clock_monitor_c



1. 简要介绍

class clock_monitor_c extends uvm_monitor;
    `uvm_component_utils( clock_monitor_c )
    // ... 后续代码 ...
endclass : clock_monitor_c

代码介绍
此代码定义了一个名为 clock_monitor_c 的类,它继承自 uvm_monitor,这是 UVM(Universal Verification Methodology)中的监控器基类。使用 uvm_component_utils 宏将该类注册到 UVM 工厂,方便在 UVM 验证环境中创建和使用该组件。该类主要用于监控时钟信号,提供了时钟监控的各种功能。

2. 接口介绍
2.1 构造函数
function new( string name, uvm_component parent );
    super.new(name, parent);
endfunction: new

代码分析

  • function new( string name, uvm_component parent );:定义类的构造函数,接收两个参数,name 是组件的名称,parent 是该组件的父组件。
  • super.new(name, parent);:调用父类 uvm_monitor 的构造函数,完成父类的初始化工作。
2.2 build_phase 函数
function void build_phase( uvm_phase phase );
    super.build_phase( phase );
    if ( !uvm_config_db #( virtual xrtl_clock_mon_vif)::get(this, "",  get_name(), m_v_clock_mon_vif ))
        `uvm_error("BUILD_PHASE", {"UNABLE to get Virtual Interface for [get_full_name]= ", get_full_name(), " from the configuration database" } );
endfunction: build_phase

代码分析

  • super.build_phase( phase );:调用父类的 build_phase 方法,确保父类在构建阶段的操作正常执行。
  • uvm_config_db #( virtual xrtl_clock_mon_vif)::get(this, "", get_name(), m_v_clock_mon_vif ):尝试从 UVM 配置数据库中获取 virtual xrtl_clock_mon_vif 类型的虚拟接口句柄。如果获取失败,使用 uvm_error 宏输出错误信息。
2.3 enable_auto_clk_monitor 函数
function void enable_auto_clk_monitor(int min_freq_Hz, int max_freq_Hz);
    m_v_clock_mon_vif.clk_min_period_ps = ((1.0/max_freq_Hz) * 1e12) + 0.5;
    m_v_clock_mon_vif.clk_max_period_ps = ((1.0/min_freq_Hz) * 1e12) + 0.5;
    if (min_freq_Hz > max_freq_Hz)
        `uvm_warning( "CLOCK MONITOR", "WARNING: MIN frequency > MAX frequency" );
    m_v_clock_mon_vif.clock_mon_enable();
    `uvm_info( "CLOCK MONITOR", $sformatf("Enabling clock monitor: %s with MIN frequency = %0d (Hz) and MAX frequency = %0d (Hz) - MIN period=%0d (ps) and MAX period=%0d (ps)", get_name(),
         min_freq_Hz, max_freq_Hz, m_v_clock_mon_vif.clk_min_period_ps, m_v_clock_mon_vif.clk_max_period_ps ), UVM_MEDIUM );
endfunction : enable_auto_clk_monitor

代码分析

  • m_v_clock_mon_vif.clk_min_period_ps = ((1.0/max_freq_Hz) * 1e12) + 0.5;:根据输入的最大频率计算时钟的最小周期(单位:皮秒),并将结果赋值给虚拟接口的相应成员。
  • m_v_clock_mon_vif.clk_max_period_ps = ((1.0/min_freq_Hz) * 1e12) + 0.5;:根据输入的最小频率计算时钟的最大周期(单位:皮秒),并将结果赋值给虚拟接口的相应成员。
  • if (min_freq_Hz > max_freq_Hz):检查最小频率是否大于最大频率,如果是,则使用 uvm_warning 宏输出警告信息。
  • m_v_clock_mon_vif.clock_mon_enable();:调用虚拟接口的 clock_mon_enable 方法,启用时钟监控功能。
  • uvm_info 宏:输出启用时钟监控的相关信息,包括监控器名称、最小和最大频率、最小和最大周期。
其他接口函数

disable_clk_monitorclk_is_stableis_clk_flatget_clk_freq_Hz 函数的逻辑类似,都是通过调用虚拟接口的方法或访问其成员变量来实现相应的时钟监控功能,并使用 UVM 宏输出相关信息。

3. 参数介绍
virtual xrtl_clock_mon_vif m_v_clock_mon_vif;
bit m_clk_mon_en;
localparam int NUMBER_OF_CLK_STABLE = 50;

代码分析

  • virtual xrtl_clock_mon_vif m_v_clock_mon_vif;:声明一个虚拟接口句柄,用于与硬件进行交互,获取时钟监控所需的信息。
  • bit m_clk_mon_en;:声明一个单比特变量,可能用于表示时钟监控的启用状态。
  • localparam int NUMBER_OF_CLK_STABLE = 50;:定义一个本地参数,用于判断时钟是否稳定的连续稳定时钟周期数。
4. 模块实现介绍
4.1 类的继承和注册
class clock_monitor_c extends uvm_monitor;
    `uvm_component_utils( clock_monitor_c )

代码分析

  • class clock_monitor_c extends uvm_monitor;:定义 clock_monitor_c 类继承自 uvm_monitor,继承后可以使用 uvm_monitor 的属性和方法。
  • uvm_component_utils( clock_monitor_c ):将 clock_monitor_c 类注册到 UVM 工厂,使得该类可以在 UVM 环境中通过工厂机制创建实例。
4.2 各阶段和功能函数

如前面接口介绍部分所述,各个函数在不同的阶段或场景下完成时钟监控的配置、启用、禁用、状态判断和频率获取等功能。这些函数通过调用虚拟接口的方法和成员变量,结合 UVM 宏输出信息,实现了完整的时钟监控流程。

5. 总结

clock_monitor_c.svh 定义了一个基于 UVM 的时钟监控器类 clock_monitor_c。它通过继承 uvm_monitor 类,利用 UVM 的配置数据库获取虚拟接口,实现了对时钟信号的监控功能。该类提供了多种接口函数,可用于配置时钟监控的频率范围、启用和禁用监控、判断时钟稳定性和获取时钟频率等。同时,使用 UVM 宏输出监控过程中的信息,方便调试和验证。整体上,该类为 UVM 验证环境提供了一个完整的时钟监控解决方案。不过,代码中可以进一步添加错误处理和更多的监控功能,以提高其健壮性和灵活性。

3. core-v-verif/lib/cv_dv_utils/uvm/clock_mon/clock_monitor_pkg.sv


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  Description : Config object to configure the clock monitor
// ----------------------------------------------------------------------------

class clock_monitor_config_c extends uvm_object;

    //////////////////////////////////////////////////////
    //Configuration Parameters 
    /////////////////////////////////////////////////////

    rand  int m_clock_monitor_enable;
    rand  int m_clock_monitor_sva_enable;

     // ------------------------------------------------------------------------
     // Utilities for the variables
     // ------------------------------------------------------------------------

    `uvm_object_utils_begin( clock_monitor_config_c )
      `uvm_field_int( m_clock_monitor_enable,     UVM_DEFAULT )
      `uvm_field_int( m_clock_monitor_sva_enable, UVM_DEFAULT )
    `uvm_object_utils_end

    function new( string name ="" );
      super.new( name );
    endfunction

endclass : clock_monitor_config_c

1. 简要介绍

class clock_monitor_config_c extends uvm_object;
    `uvm_object_utils_begin( clock_monitor_config_c )
      `uvm_field_int( m_clock_monitor_enable,     UVM_DEFAULT )
      `uvm_field_int( m_clock_monitor_sva_enable, UVM_DEFAULT )
    `uvm_object_utils_end

    rand  int m_clock_monitor_enable;
    rand  int m_clock_monitor_sva_enable;

    function new( string name ="" );
      super.new( name );
    endfunction
endclass : clock_monitor_config_c

代码介绍:此代码定义了一个名为 clock_monitor_config_c 的类,它继承自 uvm_object,这是 UVM(通用验证方法学)中的基础对象类。该类主要用于配置时钟监控器的相关参数,借助 UVM 宏完成对象的注册和字段的自动化处理,包含两个随机整型变量用于控制时钟监控器及其 SVA(SystemVerilog Assertion)的启用状态。

2. 接口介绍

2.1 构造函数
function new( string name ="" );
  super.new( name );
endfunction

代码分析

  • function new( string name ="" );:定义类的构造函数,接收一个可选的字符串参数 name,用于指定对象的名称,默认值为空字符串。
  • super.new( name );:调用父类 uvm_object 的构造函数,完成父类的初始化操作。

3. 参数介绍

rand  int m_clock_monitor_enable;
rand  int m_clock_monitor_sva_enable;

代码分析

  • rand int m_clock_monitor_enable;:声明一个随机整型变量 m_clock_monitor_enable,用于控制时钟监控器是否启用。rand 关键字表明该变量可在随机化过程中被赋值。
  • rand int m_clock_monitor_sva_enable;:声明一个随机整型变量 m_clock_monitor_sva_enable,用于控制时钟监控器的 SVA 是否启用。同样,rand 关键字使其能在随机化时被赋值。

4. 模块实现介绍

4.1 类的继承与注册
class clock_monitor_config_c extends uvm_object;
    `uvm_object_utils_begin( clock_monitor_config_c )
      `uvm_field_int( m_clock_monitor_enable,     UVM_DEFAULT )
      `uvm_field_int( m_clock_monitor_sva_enable, UVM_DEFAULT )
    `uvm_object_utils_end

代码分析

  • class clock_monitor_config_c extends uvm_object;:定义 clock_monitor_config_c 类继承自 uvm_object,从而能使用 UVM 对象的通用功能。
  • uvm_object_utils_begin( clock_monitor_config_c )uvm_object_utils_end:这组宏用于将 clock_monitor_config_c 类注册到 UVM 工厂,方便在 UVM 环境中创建和管理该类的对象。
  • uvm_field_int( m_clock_monitor_enable, UVM_DEFAULT )uvm_field_int( m_clock_monitor_sva_enable, UVM_DEFAULT ):这两个宏将类中的 m_clock_monitor_enablem_clock_monitor_sva_enable 字段注册到 UVM 的自动化处理系统中,UVM_DEFAULT 表示使用默认的处理方式,包括复制、打印、打包和解包等操作。
4.2 随机变量声明
rand  int m_clock_monitor_enable;
rand  int m_clock_monitor_sva_enable;

在类中声明随机变量,允许在 UVM 的随机化过程中为这两个变量赋不同的值,从而实现不同的时钟监控配置。

4.3 构造函数
function new( string name ="" );
  super.new( name );
endfunction

构造函数负责初始化对象,调用父类的构造函数确保父类部分正确初始化。

5. 总结

clock_monitor_config_c 类是一个基于 UVM 的配置对象类,用于管理时钟监控器及其 SVA 的启用配置。通过继承 uvm_object 类并使用 UVM 宏,实现了对象的注册和字段的自动化处理,方便在 UVM 验证环境中使用。随机变量的使用使得该配置对象能在随机化时生成不同的配置组合,增强了验证的灵活性。不过,当前类仅提供了基本的配置功能,可根据实际需求添加更多的配置参数和方法,以满足更复杂的时钟监控场景。

4. core-v-verif/lib/cv_dv_utils/uvm/clock_mon/clock_mon_vif_xrtl_pkg.sv


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  Description : Package for the clock monitor
//                
//                
// 
// ----------------------------------------------------------------------------

package clock_monitor_pkg;

    timeunit      1ps;
    timeprecision 1ps;

    import uvm_pkg::*;

    class dummy_sequence_item_c extends uvm_sequence_item;
    endclass : dummy_sequence_item_c

    class dummy_c extends uvm_sequence #(dummy_sequence_item_c);
    endclass : dummy_c

    `include "uvm_macros.svh";

    `include "clock_monitor_config_c.svh";
    `include "clock_monitor_c.svh";

endpackage : clock_monitor_pkg

5. core-v-verif/lib/cv_dv_utils/uvm/clock_mon/xrtl_clock_mon_vif.sv


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  Description : XRTL portion of clock monitor.
//
//
// ----------------------------------------------------------------------------

import uvm_pkg::*;

`include "uvm_macros.svh"

interface xrtl_clock_mon_vif ( input bit clk,             // This clock is always running
                               input bit reset_n,
                               input bit clk_to_check  );  // This clock is being checked

    //--------------------------------------------------------------------
    // Declarations
    //--------------------------------------------------------------------

    localparam time tolerance = 10ps ;    // allow 10ps of jitter

    bit clk_mon_enable;
    bit clk_mon_sva_enable = 1;

    // IEEE 1800.2017 LRM - 20.3.1 $time
    // The $time system function returns an integer that is a 64-bit unsigned value, rounded to the nearest unit

    time clk_min_period_ps;
    time clk_max_period_ps;

    time current_period;
    time last_period;

    time time_last_edge;
    time time_last_edge_pos_neg;

    time difference_in_period;          // Variable for comparing clock period

    realtime time_glitch_last_edge;
    realtime glitch_period;

    int num_consecutive_clk_stable;
    int period_error = 0;

    //--------------------------------------------------------------------
    // Always block to measure clock period
    //--------------------------------------------------------------------

    always @( posedge clk_to_check)
      begin : blk_meas_per

        if (clk_mon_enable)
          begin

            current_period = $time - time_last_edge;

            difference_in_period = (last_period - current_period);

            if ( $signed(difference_in_period) < 0 )
              begin
                difference_in_period = -$signed(difference_in_period);
              end

            ///////////////////////////////////////////////////////////////////////////////////
            // It is critical that *every* clock pulse be checked (not just every N).
            ///////////////////////////////////////////////////////////////////////////////////

            if ( difference_in_period < tolerance )
              begin
                num_consecutive_clk_stable++;
                // `uvm_info("CLOCK MONITOR", $sformatf("current_period=%0d (ps) IS equal to last_period =%0d (ps) @ time=%0t ps", current_period, last_period, $time ), UVM_DEBUG )
              end
            else
              begin
                num_consecutive_clk_stable = 1;
                // `uvm_info("CLOCK MONITOR", $sformatf("current_period=%0d (ps) is NOT equal to last_period =%0d (ps) @ time=%0t ps", current_period, last_period, $time ), UVM_DEBUG )
              end

            last_period = current_period;

            ///////////////////////////////////////////////////////////////////////////////////
            // Checking for the period being in or out of range MUST be in the interface
            ///////////////////////////////////////////////////////////////////////////////////

            if ( current_period > clk_max_period_ps )
              begin
                period_error++;
                // `uvm_info("CLOCK MONITOR", $sformatf("period_error=%0d - current_period > clk_max_period_ps", period_error ), UVM_DEBUG )
              end
            else if ( current_period < clk_min_period_ps )
              begin
                period_error++;
                // `uvm_info("CLOCK MONITOR", $sformatf("period_error=%0d - current_period < clk_min_period_ps", period_error ), UVM_DEBUG )
              end
            else
              begin
                period_error = 0;
                // `uvm_info("CLOCK MONITOR", $sformatf("period_error=%0d - current_period = clk_min_period_ps", period_error ), UVM_DEBUG )
              end

            ///////////////////////////////////////////////////////////////////////////////////
            // Report Error only once
            ///////////////////////////////////////////////////////////////////////////////////

            if (period_error == 1)
              `uvm_error("CLOCK MONITOR", $sformatf("Monitored clock period=%0d (ps) is outside the desired range of MIN period: %0d (ps) MAX period: %0d (ps) @ %m", current_period, clk_min_period_ps, clk_max_period_ps ) )


          end
        else
          begin
            num_consecutive_clk_stable = 1;
            current_period = 0;
          end

        time_last_edge = $time;

      end : blk_meas_per

    //--------------------------------------------------------------------
    // Always block to get LAST pos/neg edge
    //--------------------------------------------------------------------

    always @(clk_to_check)
      begin : blk_last_edge
        time_last_edge_pos_neg = $time;
      end   : blk_last_edge

    //--------------------------------------------------------------------
    // Functions
    //--------------------------------------------------------------------

    function time get_time_last_edge_ps();
      return time_last_edge_pos_neg;
    endfunction : get_time_last_edge_ps

    function time get_current_time_ps();
      return $time;
    endfunction : get_current_time_ps

    function void clock_mon_enable();
      clk_mon_enable = 1'b1;
    endfunction : clock_mon_enable

    function void clock_mon_disable();
      clk_mon_enable = 1'b0;
    endfunction : clock_mon_disable

    function void clock_mon_sva_enable();
      clk_mon_sva_enable = 1'b1;
    endfunction: clock_mon_sva_enable

    function void clock_mon_sva_disable();
      clk_mon_sva_enable = 1'b0;
    endfunction: clock_mon_sva_disable

    //-------------------------------------------------------------------------
    // ASSERTIONS
    //-------------------------------------------------------------------------

    // ---------------------------------------------------------------------------
    // Verify the clock is never X
    // ---------------------------------------------------------------------------

`ifndef EMULATION

    property no_x_on_ck_pll_out;
      @( posedge clk )
      disable iff ( ~reset_n  || ~clk_mon_sva_enable)
        ( !$isunknown( clk_to_check ) );
    endproperty

    no_x_on_ck_pll_out_sva : assert property (no_x_on_ck_pll_out) else
      `uvm_error("ERROR CLOCK MON SVA", "ck_pll_out MUST NEVER be X/Z")

`endif

  // ---------------------------------------------------------------------------
  // No Glitch on Output
  // ---------------------------------------------------------------------------

  // This assertion will check that there is never a glitch on the clock.
  // For the purpose of this assertion, a glitch will be defined as any 
  // '0->1->0' or '1->0->1' transition with period less than 300ps 
  // (corresponding to a max frequency of 1.666 GHz).


  always @(clk_to_check)
    begin : blk_glitch

      glitch_period         = $realtime - time_glitch_last_edge;
      time_glitch_last_edge = $realtime;
      if(clk_mon_sva_enable) begin
      	pll_out_no_glitch_assert : assert (( glitch_period > 300ps ) || ($realtime == 0)) else
        	$error("[ERROR CLOCK MON SVA] %m there is glitch of %0t (ps) which is less than 300 pico seconds", glitch_period );  //  Need to have clk0/clk1 ... uvm_error
      end 

    end : blk_glitch

endinterface : xrtl_clock_mon_vif

1. 简要介绍

//[END OF HEADER]
// ...
interface xrtl_clock_mon_vif ( input bit clk,             // This clock is always running
                               input bit reset_n,
                               input bit clk_to_check  );  // This clock is being checked
    // ...
endinterface : xrtl_clock_mon_vif

代码介绍:该代码定义了一个名为 xrtl_clock_mon_vif 的 SystemVerilog 接口,用于对时钟信号进行监控。接口接收三个输入信号:始终运行的时钟信号 clk、复位信号 reset_n 以及需要被检查的时钟信号 clk_to_check。接口内部实现了时钟周期测量、时钟信号状态检查等功能,还包含一些用于控制和获取时钟信息的函数以及断言。

2. 接口介绍

2.1 端口信号
interface xrtl_clock_mon_vif ( input bit clk,             // This clock is always running
                               input bit reset_n,
                               input bit clk_to_check  );  // This clock is being checked

代码分析

  • input bit clk:定义一个单比特输入信号 clk,表示始终运行的时钟信号,作为整个监控逻辑的参考时钟。
  • input bit reset_n:定义一个单比特输入信号 reset_n,为低电平有效的复位信号,用于对监控逻辑进行复位操作。
  • input bit clk_to_check:定义一个单比特输入信号 clk_to_check,表示需要被检查的时钟信号,是监控的主要对象。
2.2 函数接口
function time get_time_last_edge_ps();
  return time_last_edge_pos_neg;
endfunction : get_time_last_edge_ps

function time get_current_time_ps();
  return $time;
endfunction : get_current_time_ps

function void clock_mon_enable();
  clk_mon_enable = 1'b1;
endfunction : clock_mon_enable

function void clock_mon_disable();
  clk_mon_enable = 1'b0;
endfunction : clock_mon_disable

function void clock_mon_sva_enable();
  clk_mon_sva_enable = 1'b1;
endfunction: clock_mon_sva_enable

function void clock_mon_sva_disable();
  clk_mon_sva_enable = 1'b0;
endfunction: clock_mon_sva_disable

代码分析

  • get_time_last_edge_ps():返回 time_last_edge_pos_neg 的值,该值记录了 clk_to_check 信号最后一次变化的时间。
  • get_current_time_ps():返回当前的仿真时间,使用系统函数 $time 实现。
  • clock_mon_enable():将 clk_mon_enable 信号置为 1,用于启用时钟监控功能。
  • clock_mon_disable():将 clk_mon_enable 信号置为 0,用于禁用时钟监控功能。
  • clock_mon_sva_enable():将 clk_mon_sva_enable 信号置为 1,用于启用时钟监控的断言功能。
  • clock_mon_sva_disable():将 clk_mon_sva_enable 信号置为 0,用于禁用时钟监控的断言功能。

3. 参数介绍

localparam time tolerance = 10ps ;    // allow 10ps of jitter

代码分析

  • localparam time tolerance = 10ps ;:定义一个本地参数 tolerance,类型为 time,值为 10ps。该参数用于允许时钟周期存在 10ps 的抖动,在比较时钟周期时作为判断依据。

4. 模块实现介绍

4.1 时钟周期测量逻辑
always @( posedge clk_to_check)
  begin : blk_meas_per
    if (clk_mon_enable)
      begin
        current_period = $time - time_last_edge;
        difference_in_period = (last_period - current_period);
        if ( $signed(difference_in_period) < 0 )
          begin
            difference_in_period = -$signed(difference_in_period);
          end
        if ( difference_in_period < tolerance )
          begin
            num_consecutive_clk_stable++;
          end
        else
          begin
            num_consecutive_clk_stable = 1;
          end
        last_period = current_period;
        if ( current_period > clk_max_period_ps )
          begin
            period_error++;
          end
        else if ( current_period < clk_min_period_ps )
          begin
            period_error++;
          end
        else
          begin
            period_error = 0;
          end
        if (period_error == 1)
          `uvm_error("CLOCK MONITOR", $sformatf("Monitored clock period=%0d (ps) is outside the desired range of MIN period: %0d (ps) MAX period: %0d (ps) @ %m", current_period, clk_min_period_ps, clk_max_period_ps ) )
      end
    else
      begin
        num_consecutive_clk_stable = 1;
        current_period = 0;
      end
    time_last_edge = $time;
  end : blk_meas_per

代码分析

  • always @( posedge clk_to_check):这是一个时序逻辑块,在 clk_to_check 信号的上升沿触发执行。
  • if (clk_mon_enable):判断时钟监控功能是否启用。
    • current_period = $time - time_last_edge;:计算当前时钟周期,即当前时间与上一次上升沿时间的差值。
    • difference_in_period = (last_period - current_period);:计算当前周期与上一周期的差值。
    • if ( $signed(difference_in_period) < 0 ):如果差值为负,则取其绝对值。
    • if ( difference_in_period < tolerance ):如果差值小于允许的抖动范围,则连续稳定时钟周期数加 1;否则,将其重置为 1。
    • last_period = current_period;:更新上一周期的值。
    • if ( current_period > clk_max_period_ps )else if ( current_period < clk_min_period_ps ):判断当前周期是否超出了允许的最大或最小周期范围,如果超出则错误计数器加 1;否则,将错误计数器重置为 0。
    • if (period_error == 1):当错误计数器首次变为 1 时,使用 uvm_error 宏输出错误信息。
  • else:如果时钟监控功能未启用,则将连续稳定时钟周期数重置为 1,当前周期置为 0。
  • time_last_edge = $time;:更新上一次上升沿的时间。
4.2 记录最后一次边沿时间逻辑
always @(clk_to_check)
  begin : blk_last_edge
    time_last_edge_pos_neg = $time;
  end   : blk_last_edge

代码分析

  • always @(clk_to_check):这是一个组合逻辑块,在 clk_to_check 信号发生变化时触发执行。
  • time_last_edge_pos_neg = $time;:记录 clk_to_check 信号最后一次变化的时间。
4.3 断言逻辑
`ifndef EMULATION
    property no_x_on_ck_pll_out;
      @( posedge clk )
      disable iff ( ~reset_n  || ~clk_mon_sva_enable)
        ( !$isunknown( clk_to_check ) );
    endproperty

    no_x_on_ck_pll_out_sva : assert property (no_x_on_ck_pll_out) else
      `uvm_error("ERROR CLOCK MON SVA", "ck_pll_out MUST NEVER be X/Z")
`endif

always @(clk_to_check)
  begin : blk_glitch
    glitch_period         = $realtime - time_glitch_last_edge;
    time_glitch_last_edge = $realtime;
    if(clk_mon_sva_enable) begin
      pll_out_no_glitch_assert : assert (( glitch_period > 300ps ) || ($realtime == 0)) else
        $error("[ERROR CLOCK MON SVA] %m there is glitch of %0t (ps) which is less than 300 pico seconds", glitch_period );
    end 
  end : blk_glitch

代码分析

  • ifndef EMULATIONendif:条件编译指令,当未定义 EMULATION 宏时,执行中间的代码。
    • property no_x_on_ck_pll_out;:定义一个属性 no_x_on_ck_pll_out,用于检查 clk_to_check 信号是否为未知值(X 或 Z)。
      • @( posedge clk ):在 clk 信号的上升沿进行检查。
      • disable iff ( ~reset_n || ~clk_mon_sva_enable):当复位信号有效或断言功能未启用时,禁用该属性检查。
      • ( !$isunknown( clk_to_check ) ):检查 clk_to_check 信号是否为已知值。
    • no_x_on_ck_pll_out_sva : assert property (no_x_on_ck_pll_out) else:对 no_x_on_ck_pll_out 属性进行断言,如果断言失败,则使用 uvm_error 宏输出错误信息。
  • always @(clk_to_check):这是一个组合逻辑块,在 clk_to_check 信号发生变化时触发执行。
    • glitch_period = $realtime - time_glitch_last_edge;:计算当前变化与上一次变化的时间间隔,即毛刺周期。
    • time_glitch_last_edge = $realtime;:更新上一次变化的时间。
    • if(clk_mon_sva_enable):判断断言功能是否启用。
      • pll_out_no_glitch_assert : assert (( glitch_period > 300ps ) || ($realtime == 0)) else:对毛刺周期进行断言,如果毛刺周期小于 300ps 且不是仿真开始时刻,则使用 $error 函数输出错误信息。

5. 总结

xrtl_clock_mon_vif 接口实现了一个较为完整的时钟监控功能,通过对时钟周期的测量、时钟信号状态的检查以及断言机制,能够有效地监控时钟信号的稳定性和合法性。接口提供了多个函数用于控制和获取时钟监控的相关信息,方便在验证环境中使用。同时,使用本地参数和条件编译指令,增加了代码的灵活性和可维护性。然而,该接口的配置参数(如 clk_min_period_psclk_max_period_ps)需要在外部进行设置,在使用时需要确保这些参数的正确性。此外,断言部分的错误输出可以进一步统一使用 UVM 宏,以更好地集成到 UVM 验证环境中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值