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_monitor
、clk_is_stable
、is_clk_flat
和 get_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_enable
和m_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 EMULATION
和endif
:条件编译指令,当未定义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_ps
和 clk_max_period_ps
)需要在外部进行设置,在使用时需要确保这些参数的正确性。此外,断言部分的错误输出可以进一步统一使用 UVM 宏,以更好地集成到 UVM 验证环境中。