uvm_resource_db #(bit)::set("", “control”, 1, this); //where, //string scope = “”; //string name = “control”; //T val = 1; //uvm_object accessor = this; //In component_B, if(!uvm_resource_db #(bit)::read_by_name(get_full_name(), “control”, ctrl)) uvm_fatal(get_type_name(), "read_by_name failed for resource in this scope"); 你看一下我给你的这些代码 为什么UVM Resource Database中SET后面要用信号呢? 这个不是设置作用域吗? 我的理解是信号代表的是在任何作用域下都能获取到SET的control变量 那在另一个Component B中 为什么就要用ReadMyName后面加上GetFullName这样一个作用域呢? 这个信号和GetFullName到底是不是作用域的意思? 第二个问题是它们到底如何被存在UVM Resource Object 存在UVM内置的数据库中? 我记得有这样一个数据结构 就是有一个NameTable 它的键是路径 然后值是一个Q 其中每一个Q中每一个值都是一个带有Name 然后带有值 还有带有类型的一个对象 这个信号代表的作用域 还有最后这个this和control是如何拼接起来存在NameTable中的? 你看这个UVM Resource DB中SET的最后一个参数是this UVM Resource DB ReadMyName的最后一个参数是control 这就没有那个this 那SET中这个this是做什么的?
你提了几个很细致也非常核心的问题,下面我逐个解释清楚:
set("*", "control", 1, this); 中的 "*" 是作用域吗?为什么叫“信号”?和 get_full_name() 有什么关系?"*" 是一个通配符,它代表 全作用域匹配(即任意作用域)。这不是信号(signal)的意思,而是作用域的匹配模式。
在 uvm_resource_db::set("*", "control", 1, this); 中:
"*" 代表这个资源可以被 任何作用域下的组件读取。
而在 read_by_name(get_full_name(), "control", ctrl) 中,get_full_name() 返回当前组件的完整作用域路径(比如 "top.env.agent.driver"),意味着这个读取操作会根据作用域查找优先级来查找资源。
总结:"*" 是一种设定资源作用域匹配方式的方式;get_full_name() 是当前组件自己的具体作用域路径,用于读取时查找资源。
你说得对,UVM Resource 数据库的底层核心是一个叫 NameTable 的全局表。内部大致结构如下:
text
CopyEdit
NameTable : associative map key = resource name (比如 "control") value = queue/list of resource objects (uvm_resource_base 类型指针)
每个资源对象大致包含这些信息:
name(比如 “control”)
val(值,比如 1)
type(模板参数,比如 bit)
accessor(即 this,设置这个资源的组件对象)
scope(设置资源时的作用域,比如 "*")
所以,虽然你通过 "*" 设定资源适用于任意作用域,但读取的时候,系统会做作用域优先级解析(最近优先),所以即使全局可用,也可能被局部设置覆盖。
this 在 set 中的作用?为什么 read 中不需要?this 是设定资源的组件指针,代表 是谁放入了这个资源,在 debug、权限管理、traceback 时用得上。
read_by_name 中不需要 this,因为你是读取者,你告诉系统你在哪个 scope(即 get_full_name)下,系统自动查找并返回资源。
所以:
systemverilog
CopyEdit
uvm_resource_db#(bit)::set("*", "control", 1, this);
中的 this 只是表明设置者是谁。
而:
systemverilog
CopyEdit
uvm_resource_db#(bit)::read_by_name(get_full_name(), "control", ctrl);
是告诉系统:“我是某个组件,我要找一个叫 control 的资源,请根据我的作用域找一找。”
"*" 是通配符作用域,不是信号。
get_full_name() 是你读取资源时所处的实际作用域。
this 是设置资源者的引用,用于记录是谁设置的,不用于作用域匹配。
UVM Resource DB 是通过 name 映射到资源队列,然后结合作用域优先级来决定读取哪个。
如果你需要我画个结构图帮你理解资源是怎么查找的,我也可以做。
4o
那么我的第二个问题是,你之前提到在UEM Resource的NameTable中,这个Map的Key,存的是一个具体的路径,那我在这儿用Set设置的是同配符,那键会被设置成什么呢? 如果要是设置同配符的话,我在搜索的时候,Get读取的时候,又会怎么匹配呢?
非常好的问题,下面我详细解释 UVM Resource 数据库中是如何处理通配符的(重点回答你的 set 用通配符后,key 到底是什么?怎么匹配?):
key 并不是路径,而是 资源名(name)!之前可能表述有些误导,我澄清一下:
systemverilog
CopyEdit
uvm_resource_db#(bit)::set("*", "control", 1, this);
“control” 是 NameTable 中的 key。
也就是说,NameTable 实际是这样的结构:
text
CopyEdit
NameTable (resource name -> list of resource objects): "control" => [资源对象1, 资源对象2, ...]
每个资源对象内部才有:
scope(如 "*",或 "top.env.agent")
val
type
accessor
"*" 是存在哪?是怎么用的?通配符 "*" 是存在于 resource object 的 scope 字段中,而不是 Map 的 Key!
举个例子:
systemverilog
CopyEdit
uvm_resource_db#(bit)::set("*", "control", 1, this);
这行代码会把一个资源对象放到 NameTable 中 "control" 对应的队列里,这个资源对象里会记录:
name: “control”
scope: “*”
value: 1
accessor: this
read_by_name(get_full_name(), "control", ctrl) 是怎么匹配的?查找流程是这样的:
从 NameTable["control"] 中取出所有资源对象;
遍历这些资源对象,比较它们的 scope 与 get_full_name() 的匹配程度;
按照 UVM 的作用域匹配优先级,选择最匹配的那个资源;
返回它的值给 ctrl。
假设你的组件路径是:"top.env.agent.driver"
匹配优先级如下:
完全匹配(比如 scope = "top.env.agent.driver")
父路径匹配(比如 "top.env.agent"、"top.env")
通配符 "*" 最后匹配
所以 scope="*" 的资源是一个 全局 fallback,当其它作用域都找不到时才会匹配到它。
systemverilog
CopyEdit
// 设置资源 uvm_resource_db#(bit)::set("*", "control", 1, this); uvm_resource_db#(bit)::set("top.env.agent", "control", 0, this); // 某个组件 full name = "top.env.agent.driver" bit ctrl; uvm_resource_db#(bit)::read_by_name(get_full_name(), "control", ctrl);
这个时候匹配顺序如下:
优先匹配 "top.env.agent" → 命中 → 得到值是 0;
"*" 的值 1 不会被用到(因为优先级低)。
通配符 "*" 并不是当作 key 存入 NameTable;
真正的 key 是 "control"(资源名);
"*" 是资源对象的 scope 字段,存的是“在哪个作用域可用”;
读取时,会用 get_full_name() 来查找所有 control 的资源,然后根据作用域匹配规则(越精确越优先)选出最合适的那一个。
需要我把这个流程画成图表示清楚吗?
4o
好的,我接下来的问题是关于Set和Get谁先谁后执行的问题。 比如说我现在有一个UVM test,我在其中的Build Phase里面先创造了两个Component,之后呢,我再Set一些数值。 在这两个Component的Build Phase里面呢,再去Get这个数值。 实际上它会冲突吗?因为我是在Test的Build Phase中Set了,然后在Component的Build Phase里面Get了。 那我会不会说,我还没有Set结束,Component里面就先Get了?
这是个非常关键的UVM执行顺序问题,回答如下:
不会冲突!只要你在 Test 的 Build Phase 中 set(),然后在 component 的 Build Phase 中 get(),是安全的,能成功获取资源。
原因是:
UVM 所有组件的 Build Phase 是“自顶向下”执行的,Test 的 Build Phase 先于其子组件。
Build Phase 从 top(比如 test)向下递归执行每个子组件。
所以 test::build_phase() 会在 env::build_phase()、agent::build_phase()、driver::build_phase() 等之前执行。
systemverilog
CopyEdit
function void build_phase(uvm_phase phase); super.build_phase(phase); // 创建子组件 env = my_env::type_id::create("env", this); // 设置资源 uvm_resource_db#(int)::set("*", "timeout", 100, this); endfunction
create() 会触发子组件的构造函数;
但子组件的 build_phase() 还没被调用;
所以你在这里 set() 的资源,肯定会在子组件 get() 之前生效。
systemverilog
CopyEdit
function void build_phase(uvm_phase phase); super.build_phase(phase); int val; if (!uvm_resource_db#(int)::read_by_name(get_full_name(), "timeout", val)) `uvm_fatal("MY_COMP", "Resource read failed!"); endfunction
set() 已完成,所以能成功读取。如果你在 子组件的 build_phase() 之后才调用 set(),就会错过最佳时机。
比如你这样写:
systemverilog
CopyEdit
function void build_phase(uvm_phase phase); super.build_phase(phase); env = my_env::type_id::create("env", this); endfunction function void end_of_elaboration_phase(uvm_phase phase); super.end_of_elaboration_phase(phase); // 太晚了! uvm_resource_db#(int)::set("*", "timeout", 100, this); endfunction
这种写法中,子组件早在 build_phase 中就尝试 read(),但 set() 还没执行,自然会失败。
UVM 的 build_phase 是从上往下执行的,Test 的 build_phase 最先跑;
所以只要你在 Test 的 build_phase 中 set(),子组件中再 get(),就不会有先后冲突;
如果 set() 放得太晚(比如在 run_phase 或 end_of_elaboration_phase),就可能读不到了。
如有需要,我可以列出各个 phase 的执行顺序图辅助说明。
4o
好,我明白你关于UEM Resource Database的回答了 下面的问题是关于UEM Config Database的问题 什么时候我们将UEM Component Context设为Null 什么时候我们用This呢 我看很多时候Set的时候用Null,Get的时候用This 有的时候Context还会被设置为固定的一些路径 既然第二个参数InstanceName已经表示路径了 为什么我们还需要ContextName呢 我的理解对吗 你详细说一说 uvm_config_db #(bit)::set(null, “”, “control”, 1); //where, //uvm_component cntxt= null; //string inst_name = “” //String field_name = “control”; //T value = 1; // In component_B, if(!uvm_config_db #(bit)::get(this, “*”, “control”, ctrl)) uvm_fatal(get_type_name(), "get failed for resource in this scope");
你这个问题非常专业且关键,下面我详细系统地解释一下 UVM Config DB 中 cntxt(component context) 和 inst_name(instance name) 的用途、差异,以及为什么常见写法中 set(null, "*", ...) 而 get(this, "*", ...)。
uvm_config_db::set() 和 get() 的原型:systemverilog
CopyEdit
// Set uvm_config_db #(T)::set(uvm_component cntxt, string inst_name, string field_name, T value); // Get uvm_config_db #(T)::get(uvm_component cntxt, string inst_name, string field_name, output T value);
cntxt(component context)?可以理解为“我在哪里设置的”这个资源,供 UVM 后续判断这个资源对哪些组件有效。
如果设成 null,就表示**“全局设置”**,即:
不管谁来 get,我都允许访问。
inst_name?支持通配符 "*",表示所有组件路径都可以匹配。
set(null, "*", ...) 而 get(this, "*", ...)?set(null, "*", "control", 1);表示:
context = null → 表示全局设置,所有组件都能看见;
inst_name = “*” → 所有组件都允许访问这个配置;
所以等价于:“给所有组件设定了一个名为 control 的配置值,值为 1”。
get(this, "*", "control", ctrl);表示:
我是 this 组件 → 系统会获取这个组件的 get_full_name() 作为路径;
实际的作用是:“我这个组件,查一下有没有人给我(路径匹配我)设置了 control 这个值”。
cntxt?光用 inst_name 不行吗?原因在于作用域和遮蔽(shadowing)处理。
举个例子:
systemverilog
CopyEdit
// 在 test 中设置 uvm_config_db#(int)::set(env.agent, "driver", "control", 1);
这里设定的是:
context = env.agent → 只有在 env.agent 及其子路径中 get 才能看到;
inst_name = “driver” → 表示这个设置是专门给 env.agent.driver 用的。
这是一个 局部设置。
再看另一个:
systemverilog
CopyEdit
uvm_config_db#(int)::set(null, "*", "control", 0);
这个是一个 全局 fallback 设置,所有人都可以用。
查找时会从自己开始,向上回溯,看哪里有匹配的 context 和 instance name;
如果多个都匹配,优先选择设置时 context 离自己最近的那个。
| 使用方式 | 说明 | 场景 |
|---|---|---|
set(null, "*", ...) | 全局配置,无视组件层级 | 通用默认值 |
set(this, "*", ...) | 当前组件及其子组件可以看到 | 限定配置作用域,防止全局污染 |
set(some_component, "driver", ...) | 只作用于某个组件路径下 | 定向配置某个子模块 |
get(this, "*", ...) 几乎总是这么写,因为你需要告诉系统“我是谁”;
而 set() 中用不用 this 看你想让谁能访问这个配置。
systemverilog
CopyEdit
// test 中设置(全局) uvm_config_db#(int)::set(null, "*", "timeout", 10); // agent 中设置(更具体) uvm_config_db#(int)::set(this, "driver", "timeout", 5); // driver 中 get uvm_config_db#(int)::get(this, "*", "timeout", val);
此时:
driver 的路径是 top.env.agent.driver;
它会优先找到 agent 设置的那条(因为 context 是 agent,而 driver 是它的子组件);
最终读到的是 5,而不是 10。
context 控制配置值“在哪些设置组件范围内生效”,
inst_name 控制配置值“能被哪些组件路径读到”,
get() 的 this 是必须的,用来告诉 UVM 当前路径,
set() 用 null 是全局配置,用 this 是局部配置,具体看你的控制需求。
需要我画一张图来表示 ConfigDB 查找的层级结构吗?
4o
好,我觉得你没有太明白我的问题 我的问题就用你刚刚回答的第四点 那为什么还需要context 光去用instance name不行吗 用这个例子来回答吧 你来说说看 在你的例子中 你首先说set env.agent 之后呢 第二个instance name用的是driver 那我把env.agent.driver 全都放到context里面处理不行吗 或者是全都放到instance name中处理不行吗
明白了,你的问题是:
既然我们已经有了
inst_name(组件路径),为什么还需要cntxt?能不能只用其中一个就够?
非常棒的问题,我们就用这个例子来精确解构:
systemverilog
CopyEdit
// 设置一条 config uvm_config_db#(int)::set(env.agent, "driver", "timeout", 5);
你问:
👉 我可不可以改成下面这样,只用 context 或只用 inst_name,达到同样的目的?
systemverilog
CopyEdit
// 把完整路径写进 inst_name,context 为 null uvm_config_db#(int)::set(null, "top.env.agent.driver", "timeout", 5);
或:
systemverilog
CopyEdit
// 把完整路径写进 context,inst_name 为 "*" uvm_config_db#(int)::set(top.env.agent.driver, "*", "timeout", 5);
它是个 过滤器。
设置这条 config 时说:“哪些组件名(路径)能匹配这条设置”。
它决定了 作用域 和 查找优先级。
也就是:在 get() 时会从当前组件往上回溯,看哪个设置“离我最近”。
env.agent,只让 driver 用:systemverilog
CopyEdit
uvm_config_db#(int)::set(env.agent, "driver", "timeout", 5);
context 是 env.agent,表示这条配置是从 env.agent 设置的;
inst_name 是 "driver",表示仅适用于 env.agent.driver。
driver get(this, “*”, “timeout”, val):会先在自己路径 top.env.agent.driver 中查找;
向上回溯,发现 env.agent 设置了 "driver" 的 config;
匹配成功,使用它。
systemverilog
CopyEdit
uvm_config_db#(int)::set(null, "top.env.agent.driver", "timeout", 5);
这条设置是 全局的!它存在于 config 数据库的“根节点”。
UVM 的 get() 是从当前组件往上查找所有 ancestor context 的;
因为这个设置的 context 是 null,它不会出现在 driver 或其父组件的查找路径上;
所以这条设置必须明确指定完全匹配路径,否则 get 时根本查不到它;
不灵活,容易遗漏、容易冲突。
| 参数 | 含义 | 类似什么 | 控制作用 |
|---|---|---|---|
cntxt | 设置所在组件(设置作用域) | 文件夹位置 | 控制查找起点和优先级 |
inst_name | 哪些组件能用 | 文件名匹配规则 | 控制谁可以读取 |
举个类比:你设置了一个配置文件(cfg),放在某个目录(cntxt),并规定只有某些文件名(inst_name)可以用这个配置。
不能只用 inst_name,因为你无法控制“这条设置挂在哪个组件下”,也无法享受作用域优先级的便利;
不能只用 cntxt,因为你就无法选择“这条设置给谁用”,会导致所有子组件都默认继承,缺乏过滤能力;
它们是配合使用的,一个是设置范围(cntxt),一个是目标选择器(inst_name)。
需要我把整个查找流程画成一张树形图,展示 get() 时是如何回溯 context 然后匹配 inst_name 的吗?那样你会一眼看懂它为什么不能省掉。
4o
你并没有回答我的问题,你看看你在刚刚的回答中最后说的两种情况,只用context或者只用instance name的两种情况,你没有都解释到。
谢谢你指出问题,我来更清晰、系统地分别分析“只用 context” 和 “只用 inst_name” 两种情况能不能达到同样效果,并解释为什么 UVM 需要两个字段。
我们继续用这个例子分析:
systemverilog
CopyEdit
// 原始例子 uvm_config_db#(int)::set(env.agent, "driver", "timeout", 5);
systemverilog
CopyEdit
uvm_config_db#(int)::set(null, "top.env.agent.driver", "timeout", 5);
当 driver 组件调用:
systemverilog
CopyEdit
uvm_config_db#(int)::get(this, "*", "timeout", val);
它会这样查找:
从自身路径 top.env.agent.driver 开始,逐层向上(top.env.agent、top.env…)查找;
在每一层 context 上查看是否存在匹配 inst_name = "*" 的配置;
不会查到 context 为 null 的设置,除非 inst_name 完全匹配当前路径。
这个写法是可以工作的,但非常脆弱——路径必须写完整,缺乏灵活性。你一旦改变层级结构就查不到了。
systemverilog
CopyEdit
uvm_config_db#(int)::set(top.env.agent.driver, "*", "timeout", 5);
你想表达的是:“这条配置挂在 driver 上,谁来拿都能用”。
当 driver 调用:
systemverilog
CopyEdit
uvm_config_db#(int)::get(this, "*", "timeout", val);
UVM 会怎么查?
从自身路径开始,向上查找每个 ancestor context;
在每个 context 下,查看是否有 inst_name 匹配自身路径的设置;
这条设置的 context 是 driver,而调用者正好是 driver,所以匹配成功。
✅ 所以在这个场景中,这样设置也可以。
如果你在
agent或env组件中调用get(),还能读到这条配置吗?
答案是:不能,因为它们向上查不到 top.env.agent.driver 的 context。
| 用法 | 能否工作 | 灵活性 | 优先推荐? | 原因 |
|---|---|---|---|---|
set(null, full_path, ...) | ✅ 能工作 | ❌ 极差 | ❌ 不推荐 | 必须硬编码完整路径,路径一变就失效 |
set(full_context, "*", ...) | ✅ 有条件可行 | ⚠ 有限 | ⚠ 有时可用 | 只有 context == 当前路径才能匹配成功 |
set(context, relative_inst_name, ...) | ✅ ✅ ✅ | ✅ 推荐 | 支持层级查找、路径不敏感、灵活匹配 |
因为 UVM 的目标是支持 模块复用和继承配置优先级查找,单靠路径匹配无法支持以下两个核心功能:
局部设置优先级(靠 context 决定)
可以让 closer 的设置遮蔽 farther 的设置(shadowing)
路径灵活匹配(靠 inst_name 支持通配符)
可以对多个子组件设置统一配置,而不写死完整路径
两者合用才实现了:
“我在哪个模块设置了某项配置,并指明哪些子模块可以使用这项配置”。
如果你希望我画一个调用者向上查找、匹配 context + inst_name 的逻辑流程图,我可以马上做一个。这样你就能清楚看到不能只用其中一个字段的根本原因。
4o
好,那你接下来说一说UVM Resource DB和UVM Config DB的区别吧 就用一个Virtual Interface来举例 说什么情况下我可以用UVM Resource DB 什么情况下我可以用UVM Config DB 是能用UVM Resource DB的时候 我都一定可以替换成UVM Config DB吗 那它们又有什么不同呢 还是说只有在UVM Component里面我才能用UVM Config DB 你来详细说一说它们的区别吧
非常好的问题!这正是很多使用 UVM 的工程师在项目中经常混淆的地方。下面我将用一个具体的 virtual interface(虚拟接口) 的例子,详细说明:
uvm_resource_db 和 uvm_config_db 的使用场景、
是否可以互换、
内部机制和设计理念的本质区别。
| 特性/方面 | uvm_config_db | uvm_resource_db |
|---|---|---|
| 设计目的 | 配置型数据传递,用于组件之间传参数 | 通用型数据库,存储全局资源/共享对象 |
| 使用范围 | 仅适用于 uvm_component 组件系统 | 适用于 任意 uvm_object(包括非组件) |
| 支持作用域 | 是,基于组件层级(component tree) | 是,但以字符串路径为主,可任意范围控制 |
| 结构检查 | 是(类型 + 作用域检查) | 否(更宽松) |
| 可替代性 | 可以在大多数场合代替 resource_db | resource_db 通常用于 config_db 无法使用的场景 |
| 推荐度 | ✅ UVM 推荐用于组件间配置 | ⚠️ 更底层、自由,适合特定场景 |
我们想在 top-level testbench 中将 virtual interface 传给 driver 模块。
uvm_config_db 的方式(推荐):systemverilog
CopyEdit
// 在 top-level TB(如 test)中设置 virtual my_if vif_h; initial begin uvm_config_db#(virtual my_if)::set(null, "env.agent.driver", "vif", vif_h); end // 在 driver 的 build_phase 中获取 if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif)) `uvm_fatal("DRV", "virtual interface not set")
🔹 特点:
this 是一个 uvm_component,所以可以使用 config_db。
get() 是沿着组件树向上查找,支持默认值覆盖。
可以控制作用域(比如只给特定 path 设置)。
uvm_resource_db 的方式(自由但危险):systemverilog
CopyEdit
// 设置 uvm_resource_db#(virtual my_if)::set("env.agent.driver", "vif", vif_h, null); // 获取 if (!uvm_resource_db#(virtual my_if)::read_by_name(get_full_name(), "vif", vif)) `uvm_fatal("DRV", "vif not found")
🔹 特点:
以字符串路径为主,完全脱离组件体系。
可以用于非组件对象,比如一个纯 uvm_sequence。
更灵活但也更危险,因为你容易设错路径也查不出来。
| 场景 | 是否能互换 | 说明 |
|---|---|---|
| 在 component 中传递配置(如 vif、寄存器名) | ✅ 可以互换,但 config_db 更安全 | 推荐 config_db |
| 在非 component 中使用,如 sequence、reporter | ❌ config_db 无法使用 | 必须用 resource_db |
| 想设置某种全局 flag,任何地方都能访问 | ✅ resource_db 更方便 | 可考虑 resource_db |
| 需要支持作用域继承/优先级 | ❌ resource_db 不支持组件树优先级 | 只能用 config_db |
是的,uvm_config_db::get() 和 set() 都要求至少在 context 上提供 uvm_component,否则无法完成路径回溯。
如果你写:
systemverilog
CopyEdit
class my_seq extends uvm_sequence; virtual my_if vif; ... void my_func() { uvm_config_db#(...)::get(this, "", "vif", vif); // ❌ 失败! } endclass
因为 this 是一个 uvm_sequence,不是 component,所以不支持。
| 使用目标 | 推荐方式 |
|---|---|
| 在组件树中传递配置、接口、flags 等 | ✅ 用 uvm_config_db |
| 在非组件对象中读取设置(如 sequence) | ✅ 用 uvm_resource_db |
| 要实现作用域优先级(如全局默认 + 局部覆盖) | ✅ uvm_config_db 更合适 |
| 设置一个全局共享的资源(如 logger、覆盖率开关) | ✅ uvm_resource_db |
能用
uvm_config_db的地方都可以用uvm_resource_db替代,但前者更安全、结构清晰;
不能用uvm_config_db的地方,uvm_resource_db是唯一选择。