typedef class uvm_phase; // uvm_phase 类的类型定义
class m_uvm_waiter; // 定义一个内部等待器类
string inst_name; // 实例名称
string field_name; // 字段名称
event trigger; // 触发事件
function new (string inst_name, string field_name); // 构造函数
this.inst_name = inst_name; // 初始化实例名称
this.field_name = field_name; // 初始化字段名称
endfunction
endclass
typedef class uvm_config_db_options; // uvm_config_db_options 类的类型定义
//----------------------------------------------------------------------
// 类:uvm_config_db
//
// uvm_config_db#(T) 中的所有函数都是静态的,因此必须使用 :: 运算符调用它们。例如:
//
//| uvm_config_db#(int)::set(this, "*", "A");
//
// 参数值“int”将配置类型标识为 int 属性。
//
// <set> 和 <get> 方法提供与 <uvm_component> 中的 set/get_config_* 函数相同的 API 和语义。
//----------------------------------------------------------------------
class uvm_config_db#(type T=int) extends uvm_resource_db#(T); // 定义一个名为 uvm_config_db 的类,参数化类型为 T (默认是 int),继承自 uvm_resource_db#(T)
// 内部查找配置设置,以便可以重复使用
// 上下文具有一个池,该池以实例/字段名称为键。
static uvm_pool#(string,uvm_resource#(T)) m_rsc[uvm_component]; // 静态资源池,存储配置信息
// 用于 wait_modified 的内部等待器列表
static local uvm_queue#(m_uvm_waiter) m_waiters[string]; // 静态队列,存储等待器
// 函数:get
//
// 获取 ~inst_name~ 中 ~field_name~ 的值,使用组件 ~cntxt~ 作为起始搜索点。~inst_name~
// 是相对于 ~cntxt~ 的显式实例名称,如果 ~cntxt~ 是配置对象适用的实例,则可以为空字符串。
// ~field_name~ 是正在搜索的范围内的特定字段。
//
// <uvm_component> 中的基本 get_config_* 方法映射到此函数:
//
//| get_config_int(...) => uvm_config_db#(uvm_bitstream_t)::get(cntxt,...)
//| get_config_string(...) => uvm_config_db#(string)::get(cntxt,...)
//| get_config_object(...) => uvm_config_db#(uvm_object)::get(cntxt,...)
static function bit get(uvm_component cntxt, // 获取配置信息的上下文
string inst_name, // 实例名称
string field_name, // 字段名称
inout T value); // 配置值
//TBD: add file/line
int unsigned p;
uvm_resource#(T) r, rt;
uvm_resource_pool rp = uvm_resource_pool::get(); // 获取资源池
uvm_resource_types::rsrc_q_t rq; // 资源队列
if(cntxt == null) // 如果上下文为空,则使用根组件
cntxt = uvm_root::get();
if(inst_name == "") // 如果实例名称为空,则使用当前组件的完整名称
inst_name = cntxt.get_full_name();
else if(cntxt.get_full_name() != "") // 如果实例名称不为空且上下文有完整名称,则组合名称
inst_name = {cntxt.get_full_name(), ".", inst_name};
rq = rp.lookup_regex_names(inst_name, field_name, uvm_resource#(T)::get_type()); // 在资源池中查找配置信息
r = uvm_resource#(T)::get_highest_precedence(rq); // 获取优先级最高的资源
if(uvm_config_db_options::is_tracing()) // 如果开启了跟踪
m_show_msg("CFGDB/GET", "Configuration","read", inst_name, field_name, cntxt, r); // 显示获取信息
if(r == null) // 如果未找到配置信息
return 0; // 返回失败
value = r.read(cntxt); // 读取配置值
return 1; // 返回成功
endfunction
// 函数:set
//
// 创建新的或更新 ~cntxt~ 中 ~inst_name~ 的 ~field_name~ 的现有配置设置。
// 设置在 ~cntxt~ 处进行,设置的完整范围为{~cntxt~,".",~inst_name~}。如果 ~cntxt~ 为
// null,则 ~inst_name~ 提供设置的完整范围信息。~field_name~ 是目标字段。~inst_name~
// 和 ~field_name~ 都可以是 glob 样式或正则表达式样式的表达式。
//
// 如果在构建时进行设置,则使用 ~cntxt~ 层次结构来确定数据库中设置的优先级。
// 层次结构较高的级别的设置具有较高的优先级。来自同一层次结构级别的设置具有
// 最后设置获胜的语义。<uvm_resource_base::default_precedence> 用于 uvm_top,
// top 下方的每个层次结构级别递减 1。
//
// 构建时间之后,所有设置都使用默认优先级,因此具有最后获胜的语义。因此,如果在运行时,
// 低级组件对某个字段进行运行时设置,则该设置将优先于在仿真中较早进行的测试级别设置。
//
// <uvm_component> 中的基本 set_config_* 方法映射到此函数:
//
//| set_config_int(...) => uvm_config_db#(uvm_bitstream_t)::set(cntxt,...)
//| set_config_string(...) => uvm_config_db#(string)::set(cntxt,...)
//| set_config_object(...) => uvm_config_db#(uvm_object)::set(cntxt,...)
static function void set(uvm_component cntxt, // 设置配置信息的上下文
string inst_name, // 实例名称
string field_name, // 字段名称
T value); // 配置值
uvm_root top;
uvm_phase curr_phase;
uvm_resource#(T) r;
bit exists;
string lookup;
uvm_pool#(string,uvm_resource#(T)) pool;
// 处理随机数稳定性
process p = process::self();
string rstate = p.get_randstate();
top = uvm_root::get(); // 获取根组件
curr_phase = top.m_current_phase; // 获取当前阶段
if(cntxt == null) // 如果上下文为空,则使用根组件
cntxt = top;
if(inst_name == "") // 如果实例名称为空,则使用当前组件的完整名称
inst_name = cntxt.get_full_name();
else if(cntxt.get_full_name() != "") // 如果实例名称不为空且上下文有完整名称,则组合名称
inst_name = {cntxt.get_full_name(), ".", inst_name};
if(!m_rsc.exists(cntxt)) begin // 如果资源池不存在,则创建
m_rsc[cntxt] = new;
end
pool = m_rsc[cntxt]; // 获取资源池
// 插入标记以防止缓存异常
lookup = {inst_name, "__M_UVM__", field_name}; // 组合查找键
if(!pool.exists(lookup)) begin // 如果配置信息不存在,则创建新的资源
r = new(field_name, inst_name);
pool.add(lookup, r);
end
else begin // 如果配置信息已存在
r = pool.get(lookup); // 获取资源
exists = 1; // 设置已存在标志
end
if(curr_phase != null && curr_phase.get_name() == "build") // 如果当前阶段是构建阶段
r.precedence = uvm_resource_base::default_precedence - (cntxt.get_depth()); // 设置优先级
else
r.precedence = uvm_resource_base::default_precedence; // 设置默认优先级
r.write(value, cntxt); // 写入配置值
if(exists) begin // 如果配置信息已存在
uvm_resource_pool rp = uvm_resource_pool::get(); // 获取资源池
rp.set_priority_name(r, uvm_resource_types::PRI_HIGH); // 设置高优先级
end
else begin // 如果配置信息不存在
// 不存在,则将其放入资源数据库的头部。
r.set_override(); // 设置覆盖
end
// 触发任何等待器
if(m_waiters.exists(field_name)) begin // 如果有等待器
m_uvm_waiter w;
for(int i=0; i<m_waiters[field_name].size(); ++i) begin // 遍历所有等待器
w = m_waiters[field_name].get(i); // 获取等待器
if(uvm_re_match(uvm_glob_to_re(inst_name),w.inst_name) == 0) // 判断实例名称是否匹配
->w.trigger; // 触发事件
end
end
p.set_randstate(rstate); // 恢复随机数状态
if(uvm_config_db_options::is_tracing()) // 如果开启了跟踪
m_show_msg("CFGDB/SET", "Configuration","set", inst_name, field_name, cntxt, r); // 显示设置信息
endfunction
// 函数:exists
//
// 检查 ~inst_name~ 中是否可用于 ~field_name~ 的值,使用组件 ~cntxt~ 作为起始搜索点。
// ~inst_name~ 是相对于 ~cntxt~ 的显式实例名称,如果 ~cntxt~ 是配置对象适用的实例,则可以为空字符串。
// ~field_name~ 是正在搜索的范围内的特定字段。如果预期数据库中应该存在该字段,则可以将 ~spell_chk~
// 参数设置为 1 以打开拼写检查。如果存在配置参数,则函数返回 1;如果不存在,则返回 0。
static function bit exists(uvm_component cntxt, string inst_name, // 检查配置信息是否存在
string field_name, bit spell_chk=0); // 实例名、字段名,拼写检查标志
if(cntxt == null) // 如果上下文为空,则使用根组件
cntxt = uvm_root::get();
if(inst_name == "") // 如果实例名称为空,则使用当前组件的完整名称
inst_name = cntxt.get_full_name();
else if(cntxt.get_full_name() != "") // 如果实例名称不为空且上下文有完整名称,则组合名称
inst_name = {cntxt.get_full_name(), ".", inst_name};
return (uvm_resource_db#(T)::get_by_name(inst_name,field_name,spell_chk) != null); // 检查资源是否存在
endfunction
// 函数:wait_modified
//
// 等待在 ~cntxt~ 和 ~inst_name~ 中为 ~field_name~ 设置配置设置。该任务阻塞,直到应用影响指定字段的新配置设置。
static task wait_modified(uvm_component cntxt, string inst_name, // 等待配置信息被修改
string field_name); // 实例名、字段名
process p = process::self();
string rstate = p.get_randstate(); // 保存随机数状态
m_uvm_waiter waiter; // 创建等待器
if(cntxt == null) // 如果上下文为空,则使用根组件
cntxt = uvm_root::get();
if(cntxt != uvm_root::get()) begin // 如果上下文不是根组件
if(inst_name != "") // 如果实例名称不为空
inst_name = {cntxt.get_full_name(),".",inst_name}; // 组合实例名称
else
inst_name = cntxt.get_full_name(); // 使用当前组件的完整名称
end
waiter = new(inst_name, field_name); // 创建等待器
if(!m_waiters.exists(field_name)) // 如果等待器队列不存在,则创建
m_waiters[field_name] = new;
m_waiters[field_name].push_back(waiter); // 将等待器添加到队列
p.set_randstate(rstate); // 恢复随机数状态
// 等待等待器触发
@waiter.trigger;
// 从等待器列表中删除等待器
for(int i=0; i<m_waiters[field_name].size(); ++i) begin // 遍历等待器队列
if(m_waiters[field_name].get(i) == waiter) begin // 找到等待器
m_waiters[field_name].delete(i); // 删除等待器
break;
end
end
endtask
endclass
typedef uvm_config_db#(uvm_object_wrapper) uvm_config_wrapper; // uvm_config_wrapper 的类型定义
//----------------------------------------------------------------------
// 类:uvm_config_db_options
//
// 提供用于管理配置 DB 功能选项的命名空间。此类中唯一允许的是静态局部数据成员和用于
// 操作和检索数据成员值的静态函数。静态局部数据成员表示控制配置 DB 功能行为的选项和设置。
//
// 选项包括:
//
// * tracing:开启/关闭
//
// 跟踪的默认值为关闭。
//
//----------------------------------------------------------------------
class uvm_config_db_options;
static local bit ready; // 初始化标志
static local bit tracing; // 跟踪标志
// 函数:turn_on_tracing
//
// 为配置数据库打开跟踪。这会导致所有对数据库的读取和写入都显示有关访问的信息。
// 跟踪默认情况下是关闭的。
//
// 此方法由 ~+UVM_CONFIG_DB_TRACE~ 隐式调用。
static function void turn_on_tracing();
if (!ready) init(); // 如果未初始化,则进行初始化
tracing = 1; // 打开跟踪
endfunction
// 函数:turn_off_tracing
//
// 关闭配置数据库的跟踪。
static function void turn_off_tracing();
if (!ready) init(); // 如果未初始化,则进行初始化
tracing = 0; // 关闭跟踪
endfunction
// 函数:is_tracing
//
// 如果跟踪功能已打开,则返回 1;如果已关闭,则返回 0。
static function bit is_tracing();
if (!ready) init(); // 如果未初始化,则进行初始化
return tracing; // 返回跟踪状态
endfunction
static local function void init(); // 初始化函数
uvm_cmdline_processor clp; // 命令行处理器
string trace_args[$]; // 跟踪参数
clp = uvm_cmdline_processor::get_inst(); // 获取命令行处理器实例
if (clp.get_arg_matches("+UVM_CONFIG_DB_TRACE", trace_args)) begin // 检查命令行参数
tracing = 1; // 如果存在 "+UVM_CONFIG_DB_TRACE" 参数,则打开跟踪
end
ready = 1; // 设置初始化标志
endfunction
endclass
endclass
这段代码实现了一个 UVM 配置数据库 (uvm_config_db),它允许用户在 UVM 验证环境中设置和获取配置参数。让我们深入分析其代码逻辑和功能:
一、核心数据结构:
m_rsc[uvm_component]: 这是一个静态的 uvm_pool,它是配置数据库的核心。它使用 uvm_component 作为键,值为另一个 uvm_pool#(string, uvm_resource#(T))。 这意味着对于每个 UVM 组件,都维护一个独立的配置参数池。 内部的 uvm_pool 使用字符串作为键,表示配置参数的完整路径(包括实例名和字段名),值为 uvm_resource#(T),存储实际的配置参数值。 这种分层结构使得能够在不同的组件层次上设置不同的配置参数,并且可以根据层次结构解决优先级冲突。
m_waiters[string]: 这是一个静态的 uvm_queue,用于管理等待配置参数修改的等待器 (m_uvm_waiter)。键为配置参数的字段名,值为一个 m_uvm_waiter 队列。当一个配置参数被修改时,相应的等待器会被触发。
m_uvm_waiter: 这是一个简单的类,用于表示一个等待配置参数修改的等待器。它包含实例名 (inst_name)、字段名 (field_name) 和一个触发事件 (trigger)。
二、核心函数分析:
set(cntxt, inst_name, field_name, value): 这个函数用于设置配置参数。
确定范围: 它首先确定配置参数的完整范围,结合 cntxt (上下文组件)、inst_name (实例名) 和 field_name (字段名)。 如果 cntxt 为空,则使用 uvm_root。 如果 inst_name 为空,则使用 cntxt 的完整名称。
获取或创建资源: 它在 m_rsc 中查找对应的 uvm_resource#(T) 对象。如果不存在,则创建一个新的。 为了避免缓存冲突,它使用了特殊的字符串 lookup 作为键。
设置优先级: 在构建阶段 (build_phase),优先级根据组件的层次深度递减,层次高的组件优先级高。在运行阶段,所有设置都使用默认优先级,后设置的优先级高。
写入值: 它使用 r.write(value, cntxt) 将值写入 uvm_resource 对象。
设置覆盖: 如果该配置项是新创建的,则设置 override 标志,这表示该配置项覆盖了其它同名配置项。
触发等待器: 它检查是否有等待器在等待此字段的修改,如果有则触发相应的事件。
跟踪: 如果开启了跟踪 (uvm_config_db_options::is_tracing()),则显示设置信息。
get(cntxt, inst_name, field_name, value): 这个函数用于获取配置参数。
确定范围: 类似于 set 函数,它首先确定配置参数的完整范围。
查找资源: 它使用 uvm_resource_pool::lookup_regex_names 根据范围查找所有匹配的 uvm_resource 对象。
选择最高优先级: 它从找到的所有资源中选择优先级最高的资源。
读取值: 它使用 r.read(cntxt) 读取资源的值。
跟踪: 如果开启了跟踪,则显示获取信息。
exists(cntxt, inst_name, field_name, spell_chk): 这个函数用于检查配置参数是否存在。
确定范围: 类似于 set 和 get 函数,它首先确定配置参数的完整范围。
查找资源: 它使用 uvm_resource_db#(T)::get_by_name 查找配置参数,如果找到则返回 true,否则返回 false。 spell_chk 参数控制是否进行拼写检查。
wait_modified(cntxt, inst_name, field_name): 这个函数用于等待配置参数被修改。
创建等待器: 创建一个 m_uvm_waiter 对象,并将其添加到 m_waiters 队列中。
等待触发: 使用 @waiter.trigger 阻塞当前进程,直到 trigger 事件被触发(由 set 函数触发)。
移除等待器: 在等待结束后,从 m_waiters 队列中移除等待器。
三、uvm_config_db_options 类:
这个类用于管理配置数据库的跟踪选项。它提供 turn_on_tracing()、turn_off_tracing() 和 is_tracing() 函数来控制跟踪的开关,并通过 init() 函数在初始化时检查命令行参数 +UVM_CONFIG_DB_TRACE 来设置跟踪选项。
四、总结:
uvm_config_db 提供了一个强大的机制来管理 UVM 验证环境中的配置参数。它支持层次化的配置、优先级管理、以及等待配置修改的功能。 它利用了 UVM 的 uvm_pool 和 uvm_queue 等数据结构,并通过事件机制实现了异步等待。 其设计考虑了效率和线程安全,并允许灵活的配置管理。 uvm_config_db_options 类则提供了控制跟踪的便利接口。 通过合理地使用 set,get,exists 和 wait_modified 等函数,可以方便地进行参数配置和管理,提高代码的可读性和可维护性。