UVM验证(三)—UVM机制(1)

目录

(一)Factory工厂机制

[1. 工厂机制核心逻辑:"注册 - 创建 - 覆盖"](#1. 工厂机制核心逻辑:“注册 - 创建 - 覆盖”)

[2. 代码映射:从概念到实现](#2. 代码映射:从概念到实现)

[3. 实验目标:用 dadd_fixen_driver 固定 data_en=1](#3. 实验目标:用 dadd_fixen_driver 固定 data_en=1)

[4. 工厂机制的价值:"灵活验证的基石"](#4. 工厂机制的价值:“灵活验证的基石”)

[5. 常见问题与调试](#5. 常见问题与调试)

[6. 总结](#6. 总结)

(二)Phase阶段运行机制

[1. 核心规则:"三类 phase 执行顺序"](#1. 核心规则:“三类 phase 执行顺序”)

[2. 实验验证:代码与日志的映射](#2. 实验验证:代码与日志的映射)

[3. 关键细节与易错点](#3. 关键细节与易错点)

[4. 实验价值与应用场景](#4. 实验价值与应用场景)

[5. 总结](#5. 总结)

[(三)sequence 激励产生与交互执行机制](#(三)sequence 激励产生与交互执行机制)

[1. sequence 核心机制概述](#1. sequence 核心机制概述)

[2. sequence_item的发送](#2. sequence_item的发送)

[2.1 核心规则:sequence_item 发送的三种方法](#2.1 核心规则:sequence_item 发送的三种方法)

[2.2 逐类解析:代码逻辑与实验验证](#2.2 逐类解析:代码逻辑与实验验证)

[2.3 关键细节与对比](#2.3 关键细节与对比)

[2.4 实验价值与总结](#2.4 实验价值与总结)

[3. sequence 的发送](#3. sequence 的发送)

[3.1 核心规则:子 sequence 发送的三类方法](#3.1 核心规则:子 sequence 发送的三类方法)

[3.2 逐类解析:结合 dadd 代码与脚本](#3.2 逐类解析:结合 dadd 代码与脚本)

[3.3 关键细节与对比](#3.3 关键细节与对比)

[3.4 实验价值与总结](#3.4 实验价值与总结)


(一)Factory工厂机制

1. 工厂机制核心逻辑:"注册 - 创建 - 覆盖"

UVM 工厂机制本质是 "对象创建的集中管控",核心解决两个问题:

(1)解耦创建逻辑 :让对象的 "创建" 与 "使用" 分离,无需硬编码 new(),便于后续替换实现(如用 dadd_fixen_driver 替换 dadd_driver )。

(2)支持动态替换 :通过 set_type_overrideset_inst_override,可在不修改代码的前提下,替换组件类型(如把 dadd_driver 换成 dadd_fixen_driver ),灵活控制验证行为。

2. 代码映射:从概念到实现

(1) 注册(uvm_component_utils

  • 作用:把类 "登记" 到 UVM 工厂的 "查找表",让工厂能识别并创建它。
  • 代码示例dadd_driver 注册):
cpp 复制代码
class dadd_driver extends uvm_driver #(dadd_item);
    `uvm_component_utils(dadd_driver)  // 注册 component 到工厂
    // ... 类定义 ...
endclass
  • 关键
    • uvm_component_utils 是注册 component(继承 uvm_component 的类,如 drivermonitor )的宏;若为 object(继承 uvm_object 的类,如 sequence_item ),则用 uvm_object_utils
    • 注册后,类会被加入 UVM 工厂的 "类型映射表",后续可通过 type_id::create() 创建实例。

(2)创建(type_id::create

  • 作用:通过工厂创建对象,自动检查是否有 "类型覆盖",再决定实例化原类还是替换类。
  • 代码示例dadd_iagent 中创建 dadd_driver ):
cpp 复制代码
function dadd_iagent::new(string name = "dadd_iagent", uvm_component parent);
    super.new(name, parent);
    // 通过工厂创建 driver,而非直接 new(dadd_driver::new(...))
    drv = dadd_driver::type_id::create("drv", this);  
    // ... 其他组件创建 ...
endfunction
  • 关键
    • type_id::create("drv", this) 会先查工厂是否有 dadd_driver 的覆盖类型(如 dadd_fixen_driver ),若有则创建覆盖类,否则创建原类。
    • 替换 new()create() 是实现 "动态覆盖" 的核心:后续只需调用 set_type_override,无需修改 dadd_iagent 代码,就能替换 driver 类型。

(3)覆盖(set_type_override

  • 作用 :告诉工厂 "用类 B 替换类 A 的创建",实验中把 dadd_driver 换成 dadd_fixen_driver,固定 data_en=1
  • 代码示例 (在 dadd_rand_test 中覆盖 ):
cpp 复制代码
class dadd_rand_test extends uvm_test;
    `uvm_component_utils(dadd_rand_test)
    function void build_phase(uvm_phase phase);
        // 全局覆盖:所有 dadd_driver 类型都替换为 dadd_fixen_driver
        dadd_driver::type_id::set_type_override(dadd_fixen_driver::get_type());  
        super.build_phase(phase);
    endfunction
endclass
  • 关键
    • set_type_override 需在 build_phase 调用,确保工厂在创建组件前生效。
    • 替换后,dadd_iagentdrv = dadd_driver::type_id::create(...) 会自动创建 dadd_fixen_driver 实例,实现 "无代码修改替换组件"。

3. 实验目标:用 dadd_fixen_driver 固定 data_en=1

dadd_driverdata_en 是随机的,实验通过 "工厂覆盖" 替换为 dadd_fixen_driver,强制 data_en=1

(1)dadd_fixen_driver 逻辑

cpp 复制代码
task dadd_fixen_driver::main_phase(uvm_phase phase);
    wait(tb_dadd.dadd_if.reset_n);
    forever begin
        seq_item_port.get_next_item(req);
        @(posedge tb_dadd.dadd_if.clk);
        // 固定 data_en=1(与原 driver 的随机逻辑区分)
        tb_dadd.dadd_if.mcb.dadd_in_en <= 1'b1;  
        tb_dadd.dadd_if.mcb.dadd_in     <= req.data;
        tb_dadd.dadd_if.mcb.dadd_in_addr<= req.addr;
        seq_item_port.item_done();
    end
endtask
  • 关键dadd_in_en <= 1'b1 硬编码为 1,覆盖原 driver 的随机行为。

(2)覆盖流程

  • 在 dadd_rand_test 的 build_phase 调用 set_type_override,替换 dadd_driver 为 dadd_fixen_driver。
  • dadd_iagent 中通过 dadd_driver::type_id::create() 创建 driver 时,工厂自动实例化 dadd_fixen_driver。
  • 仿真时,driver 驱动 DUT 的 data_en 恒为 1,实现实验目标。

4. 工厂机制的价值:"灵活验证的基石"

(1)解耦与复用 :组件创建逻辑与使用逻辑分离,dadd_iagent 无需关心 driver 具体类型,只需通过工厂创建,复用性更高。

(2)动态配置 :通过一行 set_type_override,就能切换 driver 行为(随机 / 固定 data_en ),无需修改 agentenv 代码。

(3)可维护性 :验证需求变化时(如替换 driver 协议),只需修改 driver 子类和覆盖逻辑,不影响上层组件。

5. 常见问题与调试

(1)覆盖不生效

  • 检查 set_type_override 是否在 build_phase 调用(main_phase 调用无效,因组件已创建 )。
  • 检查 create() 是否用 type_id::create,而非直接 new()(直接 new() 会绕过工厂,覆盖失效 )。

(2)类型注册遗漏

  • 若子类(如 dadd_fixen_driver )未注册(忘记 uvm_component_utils 宏 ),工厂无法识别,覆盖会失败。

(3)层次化覆盖

  • 若需只替换某个 agent 中的 driver(而非全局替换 ),可用 set_inst_override,指定实例路径(如 uvm_test_top.env.iagt.drv )。

6. 总结

UVM 工厂机制通过 "注册 - 创建 - 覆盖" 三步,实现了:

  • 动态替换组件 :实验中用 dadd_fixen_driver 替换 dadd_driver,无需修改 agent 代码。
  • 解耦创建逻辑:组件创建由工厂统一管控,上层组件只需关注接口,无需关心具体实现。
  • 灵活验证配置 :一行代码切换验证行为(随机 / 固定 data_en ),让验证平台可适配不同场景。

这是 UVM 实现 "可复用、可配置验证平台" 的核心机制,掌握后能大幅提升验证环境的扩展性与维护性。

(二)Phase阶段运行机制

1. 核心规则:"三类 phase 执行顺序"

UVM phase 执行顺序分为 "组件内顺序""树形结构顺序""同级组件顺序" 三类,需结合实验场景理解:

(1)同一组件内的 phase 顺序(纵向顺序)

  • 规则 :同一组件(如 dadd_iagent )内,phase 按 "功能阶段" 顺序执行,从 build_phase 开始,到 final_phase 结束,流程为:

    build_phase → connect_phase → end_of_elaboration_phase → start_of_simulation_phase →
    reset_phase → configure_phase → run_phase → main_phase → shutdown_phase →
    extract_phase → check_phase → report_phase → final_phase

  • 实验映射 :若在 dadd_driver 的所有 phase 中加入打印,会看到该组件内 phase 严格按上述顺序执行。

(2) 树形结构中的 phase 顺序(横向跨组件)

UVM 组件构成 树形层次结构uvm_test_top → env → agent → driver/monitor ),同一类型 phase 在树形结构中的执行顺序分两种:

phase 类型 执行方向 典型 phase 举例 实验流程映射(以 build_phaseconnect_phase 为例)
自上而下(Top-Down) 从根到叶子 build_phasefinal_phase uvm_test_top.build_phaseenv.build_phaseagent.build_phasedriver.build_phase
自下而上(Bottom-Up) 从叶子到根 connect_phasereport_phase driver.connect_phaseagent.connect_phaseenv.connect_phaseuvm_test_top.connect_phase

(3)同级组件的 phase 顺序(横向同层)

  • 规则 :同一父组件下的 同级组件 (如 agent 内的 drivermonitorsequencer ),同一 phase 的执行顺序按 "实例化名称的字典序" 执行。

  • 实验映射 :若 agentdriver 命名为 drvmonitor 命名为 imonsequencer 命名为 sqr,则 build_phase 执行顺序为:

    drv.build_phase → imon.build_phase → sqr.build_phase

(因字典序 drv < imon < sqr ,按字母顺序排列 )

2. 实验验证:代码与日志的映射

(1)代码中加入 phase 打印(以 dadd_driver 为例)

cpp 复制代码
class dadd_driver extends uvm_driver #(dadd_item);
    `uvm_component_utils(dadd_driver)
    function new(string name="dadd_driver", uvm_component parent);
        super.new(name, parent);
    endfunction

    task build_phase(uvm_phase phase);
        `uvm_info("DRV", "build_phase executed", UVM_LOW)
        super.build_phase(phase);
    endtask

    task connect_phase(uvm_phase phase);
        `uvm_info("DRV", "connect_phase executed", UVM_LOW)
        super.connect_phase(phase);
    endtask
    // ... 其他 phase 同理加入打印 ...
endclass

(2)日志分析(以 build_phaseconnect_phase 为例)

build_phase 日志(自上而下)

复制代码
UVM_INFO dadd_driver.sv(10) @ 0: uvm_test_top.env.iagt.drv [DRV] build_phase executed
UVM_INFO dadd_imonitor.sv(10) @ 0: uvm_test_top.env.iagt.imon [MON] build_phase executed
UVM_INFO dadd_sequencer.sv(10) @ 0: uvm_test_top.env.iagt.sqr [SQR] build_phase executed
UVM_INFO dadd_oagent.sv(10) @ 0: uvm_test_top.env.oagt [OAGT] build_phase executed
  • 逻辑 :先执行 uvm_test_topbuild_phase(未完整打印 ),再执行 envbuild_phase,然后按字典序执行 iagt 内的 drvimonsqr,最后执行 oagt(因 iagt 字典序小于 oagt )。

connect_phase 日志(自下而上)

复制代码
UVM_INFO dadd_driver.sv(15) @ 0: uvm_test_top.env.iagt.drv [DRV] connect_phase executed
UVM_INFO dadd_imonitor.sv(15) @ 0: uvm_test_top.env.iagt.imon [MON] connect_phase executed
UVM_INFO dadd_sequencer.sv(15) @ 0: uvm_test_top.env.iagt.sqr [SQR] connect_phase executed
UVM_INFO dadd_oagent.sv(15) @ 0: uvm_test_top.env.oagt [OAGT] connect_phase executed
UVM_INFO dadd_env.sv(15) @ 0: uvm_test_top.env [ENV] connect_phase executed
UVM_INFO dadd_test.sv(15) @ 0: uvm_test_top [TEST] connect_phase executed
  • 逻辑 :先执行叶子组件(drvimonsqr ),再执行父组件(oagtenvuvm_test_top ),符合 "自下而上" 规则。

3. 关键细节与易错点

(1) run_phasemain_phase 的关系

  • run_phasetask phase 的父 phase,main_phaserun_phase 的子 phase(属于 task phase 类别 )。
  • 执行顺序:run_phase 启动后,main_phase 会自动执行,且遵循 自下而上 规则(如先 driver.main_phase,再 agent.main_phase 等 )。

(2)字典序的具体表现

  • 同级组件的 phase 执行顺序,严格按 "new 时的名称字符串比较" ,如:
    • 名称为 a_drvb_drva_drv 先执行(因 a 的 ASCII 码小于 b )。
    • 名称为 drv1drv2drv1 先执行(数字 1 的 ASCII 码小于 2 )。

(3)phase 阻塞与 objection 机制

  • task phase(如 main_phaserun_phase )需要通过 phase.raise_objectionphase.drop_objection 控制仿真进度,否则仿真会直接结束。
  • function phase(如 build_phaseconnect_phase )是纯函数,无需 objection 机制,执行完立即退出。

4. 实验价值与应用场景

(1)验证平台构建

  • build_phase 自上而下 :确保父组件先完成 "资源分配"(如创建子组件 ),子组件再初始化(如 driveragent.build_phase 中被创建 )。
  • connect_phase 自下而上 :确保子组件先完成 "端口连接"(如 driver.seq_item_port 连接 sequencer ),父组件再做全局连接(如 agent 连接 scoreboard )。

(2)调试与问题定位

  • 若子组件的 build_phase 未执行,需检查父组件是否在 build_phase 中正确创建了它(因 build_phase 自上而下,父组件未创建则子组件无法执行 )。
  • connect_phase 逻辑异常,需检查是否因 "自下而上" 顺序导致,子组件的端口未准备好时父组件已开始连接。

(3) 复杂场景控制

  • 对于多 agent、多 sequence 的验证平台,利用 字典序控制同级组件执行顺序 ,可确保特定组件优先执行(如 monitor 先采样,driver 后驱动 )。

5. 总结

UVM phase 执行顺序的核心逻辑可归纳为:

  • 同一组件内 :按功能阶段顺序(buildconnectrun 等 )依次执行。
  • 树形结构中build_phasefinal_phase 自上而下,其余 phase 自下而上。
  • 同级组件间:按实例化名称的字典序执行。

理解这三类顺序,能精准控制验证平台的 组件初始化流程端口连接时机激励注入顺序,是构建复杂 UVM 验证环境的基础。实验中通过打印各 phase 执行日志,可直观验证这些规则,为调试和优化验证平台提供依据。

(三)sequence 激励产生与交互执行机制

1. sequence 核心机制概述

在 UVM 中,sequence 机制是激励产生、调度与驱动的核心,通过 sequencesequencerdriver 的协作,实现 "激励生成→仲裁调度→信号驱动→结果反馈" 的完整闭环。以下从 执行规则代码映射实验验证 三个维度解析其核心逻辑:

(1)执行规则

  • 角色分工
    • sequence:作为 "激励生成器",负责创建、随机化 sequence_item(事务包),并通过 sequencer 发送给 driver
    • sequencer:作为 "调度中心",接收多个 sequence 的请求,通过仲裁算法(如 FIFO、优先级)决定发送顺序,再转发给 driver
    • driver:作为 "执行者",从 sequencer 获取 sequence_item,转换为物理信号驱动 DUT,并可通过 response 反馈结果。
  • 交互流程 (完整握手):
    • sequence 生成 sequence_item 并随机化,通过 start_item/finish_item 提交给 sequencer
    • sequencer 仲裁后将 item 放入 REQ_FIFOdriver 通过 get_next_item 取走并驱动 DUT。
    • 若需反馈,driver 生成 response 放入 RSP_FIFOsequence 通过 get_response 获取结果,完成生命周期。

(2)总结

  • sequence 专注于 "产生什么激励",支持随机化和约束,覆盖多样化测试场景。
  • sequencer 专注于 "何时发送激励",通过仲裁算法协调多 sequence 竞争。
  • driver 专注于 "如何驱动激励",将抽象事务转换为物理信号,确保时序正确

2. sequence_item的发送

sequence 的执行必须在 task body 中执行,task body 是在 task phase 中自动调用的。

2.1 核心规则:sequence_item 发送的三种方法

在 UVM 中,sequence 发送 sequence_item(事务包,如 dadd_item )有三类典型方法,本质都是 "实例化→随机化→发送给 sequencer" 的流程封装,但语法和复杂度不同:

方法分类 核心语法 封装层级 适用场景
基础方法 start_item + finish_item 最底层,无封装 需精准控制发送流程(如调试)
宏封装方法 uvm_create + uvm_send 封装 new + start_item/finish_item 需灵活指定 sequencer
高级宏(常用) uvm_do 系列宏 封装 uvm_create + 完整流程 日常验证(简洁高效)
2.2 逐类解析:代码逻辑与实验验证

以下结合 dadd 验证平台的 dadd_rand_sequence.sv 代码和 Makefile 脚本,说明每种方法的细节:

(1)方法 1:start_item + finish_item(基础流程)

执行规则 :手动完成 sequence_item"实例化→连接 sequencer→随机化→发送" 全流程,每一步需显式调用:

步骤 代码逻辑 作用说明
1. 实例化 item item = new("item"); 创建 dadd_item 事务对象,准备承载激励数据
2. 连接 sequencer start_item(item); itemiagt.sqr(输入 agentsequencer )建立调度连接
3. 随机化 item item.randomize(); 随机生成 addrdatadata_en 等字段,模拟真实激励
4. 发送给 driver finish_item(item); 通知 sequencer 完成调度,将 item 转发给 driver 驱动 DUT

代码映射dadd_sequence.svSTART_ITEM 分支 ):

cpp 复制代码
`ifdef START_ITEM
task body();
  if(starting_phase != null) 
    starting_phase.raise_objection(this);  // 阻止仿真提前结束
  repeat(20) begin  // 发送20个事务
    item = new("item");         // 1. 实例化
    start_item(item);           // 2. 连接sequencer
    item.randomize();           // 3. 随机化
    finish_item(item);          // 4. 发送给driver
  end
  if(starting_phase != null) 
    starting_phase.drop_objection(this);  // 允许仿真结束
endtask : body
`endif

实验验证 (执行 make send_item_start_item ):

  • 日志显示 20 次 item 发送,每次含随机化的 addrdata。

  • 波形中 dadd_if 接口的信号(如 addrdata )与日志匹配,证明 driver 正确驱动 DUT。

    UVM_INFO dadd_sequence.sv(12) @ 100ns: uvm_test_top.env.iagt.sqr [SEQ] Sent item: addr=0x12, data=0x34, data_en=1

(2)方法 2:uvm_create + uvm_send(宏封装基础流程)

执行规则 :用 uvm_create 替代 new 实例化 item,用 uvm_send 替代 start_item/finish_item 发送,本质是 封装了基础方法的语法糖 ,但更灵活(可指定 sequencer ):

步骤 代码逻辑 作用说明
1. 实例化 item uvm_create(item);uvm_create_on(item, sqr); 不仅创建 item,还可指定发送到哪个 sequencer(如 iagt.sqroagt.sqr
2. 随机化 item item.randomize(); 同方法 1
3. 发送给 driver uvm_send(item); 封装 start_item/finish_item,简化发送流程

代码映射dadd_sequence.svUVM_CREATE 分支 ):

cpp 复制代码
`elsif UVM_CREATE
task body();
  if(starting_phase != null) 
    starting_phase.raise_objection(this);
  repeat(20) begin
    `uvm_create(item);  // 1. 实例化(可指定sequencer)
    item.randomize();   // 2. 随机化
    `uvm_send(item);    // 3. 发送(封装start_item/finish_item)
  end
  if(starting_phase != null) 
    starting_phase.drop_objection(this);
endtask : body
`endif

实验验证 (执行 make send_item_uvm_create ):

  • 日志与方法 1 类似,但代码更简洁,uvm_create/uvm_send 隐式完成连接和发送。
  • 若修改为 uvm_create_on(item, env.oagt.sqr);item 会发送到 oagtsequencer,波形中 oagt 接口信号变化,验证跨 agent 发送。

(3)方法 3:uvm_do 系列宏(高级封装,最常用)

执行规则
uvm_do"一站式" 宏 ,直接封装 "实例化→随机化→发送" 全流程,甚至可带约束(uvm_do_with )或优先级(uvm_do_pri ),是日常验证最常用的方法:

宏类型 语法示例 作用说明
基础发送 uvm_do(item); 自动完成实例化、随机化、发送
带约束发送 uvm_do_with(item, {item.data_en==1;}); 随机化时固定 data_en=1,其他字段随机
指定 sequencer 发送 uvm_do_on(item, env.iagt.sqr); 强制 item 发送到 iagtsequencer

代码映射dadd_sequence.svUVM_DO 分支 ):

cpp 复制代码
`else//UVM_DO
task body();
  if(starting_phase != null) 
    starting_phase.raise_objection(this);
  repeat(20) begin
    `uvm_do(item)  // 一站式完成实例化、随机化、发送
  end
  if(starting_phase != null) 
    starting_phase.drop_objection(this);
endtask : body
`endif

实验验证 (执行 make send_item_uvm_do ):

  • 日志与前两种方法一致,但代码行数最少,uvm_do 隐式完成所有步骤。
  • 若修改为 uvm_do_with(item, {item.addr==0x5a5a;});,日志中 addr 固定为 0x5a5a,验证约束生效。
2.3 关键细节与对比

(1)方法选择建议

  • 调试阶段 :用 start_item/finish_item,逐行控制流程,方便定位问题。
  • agent 发送 :用 uvm_create_onuvm_do_on,明确指定 sequencer,避免发送到错误 agent
  • 日常验证 :优先用 uvm_do 系列宏,代码简洁,减少样板代码。

(2)易错点

  • starting_phase 为空 :若 sequence 未关联 phase(如未在 test 中设置 starting_phase ),raise_objection 会报错,需确保 sequence.starting_phase = phase;
  • uvm_create 未指定 sequencer :若未用 uvm_create_onp_sequencer 未正确连接,item 可能发送到 null sequencer,触发 UVM_ERROR
2.4 实验价值与总结

通过 Makefile 脚本切换宏定义(START_ITEM/UVM_CREATE/UVM_DO ),可直观对比三种方法的执行流程:

  • 证明三类方法本质是同一流程的不同封装,uvm_do 是最简洁的高阶用法。
  • 验证平台可灵活切换发送方式,适配不同测试场景(如调试、跨 agent 、带约束发送 )。

3. sequence 的发送

3.1 核心规则:子 sequence 发送的三类方法

sequence 需发送sequence (如 dadd_fixen_sequence 调用 dadd_rand_sequence )时,本质是 "父 sequence 调度子 sequence 的生命周期",三类方法的核心差异在于 "调度的封装层级":

方法分类 核心语法 封装层级 适用场景
start 函数 seq.start(p_sequencer); 最底层,手动控制实例化、随机化、启动 需精准控制子 sequence 流程
uvm_create/uvm_send uvm_create(seq); + uvm_send(seq); 封装 start 函数,简化调用 需显式控制随机化步骤
uvm_do uvm_do_with(seq, {约束;}) 封装 "实例化 + 随机化 + 启动" 全流程 日常验证(简洁高效)
3.2 逐类解析:结合 dadd 代码与脚本

以下基于 dadd_fixen_sequence.svdadd_rand_sequence.sv 代码,说明每种方法的细节:

(1)方法 1:start 函数(手动调度子 sequence

执行规则

手动完成子 sequence"实例化→随机化约束→启动" 全流程,需显式调用 start 函数关联 p_sequencer(父 sequence 所在的 sequencer )。

步骤 代码逻辑 作用说明
1. 实例化子 seq seq = dadd_rand_sequence::type_id::create("seq"); 创建子 sequence 对象(dadd_rand_sequence
2. 随机化约束 seq.randomize() with {data_en_rand == 1;}; 固定子 sequencedata_en_rand 为 1,间接约束 dadd_item.data_en=1
3. 启动子 seq seq.start(p_sequencer); 让子 sequence 挂载到父 sequencesequenceriagt.sqr )上

代码映射dadd_fixen_sequence.svSEND_SEQ 分支 ):

cpp 复制代码
`ifdef SEND_SEQ
`ifdef START
task body();
  if(starting_phase != null) 
    starting_phase.raise_objection(this);  // 阻止仿真提前结束

  // 1. 实例化子 sequence
  seq = dadd_rand_sequence::type_id::create("seq");  
  // 2. 约束子 sequence 的 data_en_rand 为 1
  seq.randomize() with {data_en_rand == 1;};  
  // 3. 启动子 sequence,挂载到 p_sequencer(iagt.sqr)
  seq.start(p_sequencer);  

  if(starting_phase != null) 
    starting_phase.drop_objection(this);  // 允许仿真结束
endtask : body
`endif
`endif

实验验证 (执行 make send_seq_start ):

  • 日志显示子 sequence 被启动,且 data_en 固定为 1:

    plaintext

    复制代码
    UVM_INFO dadd_rand_sequence.sv(20) @ 100ns: uvm_test_top.env.iagt.sqr [SEQ] Sent item: data_en=1, addr=0x12, data=0x34
  • 波形中 dadd_if.data_en 恒为 1,证明约束生效。

(2)方法 2:uvm_create + uvm_send(封装 start 函数)

执行规则

uvm_create 替代手动 new 实例化子 sequence,用 uvm_send 替代 start 函数,封装部分流程,但仍需手动随机化。

步骤 代码逻辑 作用说明
1. 实例化子 seq uvm_create(seq); 隐式创建子 sequence,并关联到 p_sequencer
2. 随机化约束 seq.randomize() with {data_en_rand == 1;}; 同方法 1
3. 启动子 seq uvm_send(seq); 封装 start 函数,简化发送流程

代码映射dadd_fixen_sequence.svUVM_CREATE 分支 ):

cpp 复制代码
`elsif UVM_CREATE
task body();
  if(starting_phase != null) 
    starting_phase.raise_objection(this);

  // 1. 实例化子 sequence(隐式关联 p_sequencer)
  `uvm_create(seq);  
  // 2. 约束子 sequence
  seq.randomize() with {data_en_rand == 1;};  
  // 3. 发送子 sequence(封装 start 函数)
  `uvm_send(seq);  

  if(starting_phase != null) 
    starting_phase.drop_objection(this);
endtask : body

实验验证 (执行 make send_seq_uvm_create ):

  • 日志与方法 1 类似,但代码更简洁,uvm_create/uvm_send 隐式完成部分流程。
  • 若删除 seq.randomize()data_en_rand 会随机化,验证 uvm_create 不自动随机化,需手动调用。

(3)方法 3:uvm_do 系列宏(一站式封装)

执行规则
uvm_do_with一站式封装 "实例化 + 随机化 + 启动" 全流程,甚至可在宏内直接写约束,无需手动调用 randomize

步骤 代码逻辑 作用说明
1. 实例化 + 随机化 + 启动 uvm_do_with(seq, {data_en_rand == 1;}); 隐式完成 "创建→随机化(带约束)→启动",最简洁

代码映射dadd_fixen_sequence.svUVM_DO 分支 ):

cpp 复制代码
`else//UVM_DO
task body();
  if(starting_phase != null) 
    starting_phase.raise_objection(this);

  // 一站式完成:实例化+随机化(约束 data_en_rand=1)+启动
  `uvm_do_with(seq, {data_en_rand == 1;});  

  if(starting_phase != null) 
    starting_phase.drop_objection(this);
endtask : body

实验验证 (执行 make send_seq_uvm_do ):

  • 日志与前两种方法一致,但代码行数最少,uvm_do_with 隐式完成所有步骤。
  • 若修改约束为 {data_en_rand == 0;},波形中 data_en 恒为 0,验证宏内约束生效。
3.3 关键细节与对比

(1)方法选择建议

  • 调试子 sequence :用 start 函数,逐行控制实例化、随机化、启动,方便定位问题(如约束不生效时,检查 randomize 调用 )。
  • 需显式随机化 :用 uvm_create/uvm_send,手动控制随机化时机(如先随机化部分字段,再覆盖其他约束 )。
  • 日常嵌套发送 :优先用 uvm_do_with,代码最简洁,减少样板代码,适合高频使用。

(2)易错点

  • p_sequencer 未关联 :若子 sequence 未通过 uvm_declare_p_sequencer 关联父 sequencerp_sequencer 会空指针报错,需确保:

    systemverilog

    复制代码
    `uvm_declare_p_sequencer(dadd_sequencer)  // 在子 sequence 中声明
  • 约束未生效 :若 uvm_do_with 中约束语法错误(如 data_en_rand = 1; 少写 == ),约束会被忽略,需检查约束表达式。

3.4 实验价值与总结

通过 Makefile 脚本切换方法(send_seq_start/send_seq_uvm_create/send_seq_uvm_do ),可直观对比三类方法的执行流程:

  • 证明三类方法本质是同一流程的不同封装,uvm_do 是最简洁的高阶用法。
  • 验证平台可灵活切换子 sequence 的调度方式,适配不同测试场景(如调试、高效开发、复杂约束 )。

掌握这三类方法,可高效实现 "父 sequence 调度子 sequence" 的嵌套逻辑,是构建复杂验证场景(如 "先复位子 sequence,再随机读写子 sequence" )的基础。

相关推荐
一只迷茫的小狗3 个月前
关于systemverilog中在task中使用force语句的注意事项
systemverilog
S&Z34634 个月前
[PRO_A7] SZ501 FPGA开发板简介
fpga开发·systemverilog
-interface5 个月前
25届数字IC验证秋招总结
秋招·uvm·ic验证
lj想拿50w5 个月前
脚本学习(1)验证目录自动化生成脚本
python·脚本·ic验证
m0_713541846 个月前
验证环境中为什么要用virtual interface
systemverilog
lj想拿50w6 个月前
systemverilog刷题小记
systemverilog·ic验证
bitlogic6 个月前
理解 SystemVerilog 中的循环与并发线程
verilog·systemverilog·scope·verification·fpga & design·lifetime·并发线程
m0_713541847 个月前
systemverilog中的force,release和assign
systemverilog
m0_713541848 个月前
systemverilog中的priority if
systemverilog