UVM 序列(Sequence)组织详解
UVM 的 sequence 机制是整个验证平台产生激励的核心。它把“激励的产生”和“激励的驱动”彻底解耦:sequence 负责产生事务,driver 负责把事务变成 pin 级信号。理解 sequence 的组织方式,是搭建可复用、可扩展验证平台的关键。
下面从基本概念到高级组织方式,系统讲一遍。
一、基本概念
1.1 三个核心角色
| 角色 | 作用 |
|---|---|
uvm_sequence_item | 事务基类,描述一次传输的数据 |
uvm_sequence | 序列基类,负责产生 item 并发送 |
uvm_sequencer | 仲裁器,连接 sequence 和 driver |
三者关系:
sequence → sequencer → driver → DUT ↑ │ └──────────── response ────────────────┘1.2 一个最简单的 sequence
class my_seq extends uvm_sequence #(my_item); `uvm_object_utils(my_seq) function new(string name = "my_seq"); super.new(name); endfunction task body(); my_item req; repeat (10) begin req = my_item::type_id::create("req"); start_item(req); if (!req.randomize()) `uvm_error("SEQ", "randomize failed") finish_item(req); end endtask endclassbody()是 sequence 的执行体,每次start()都会调用它。
1.3 item 与 sequence 的区别
- item:一次事务,比如一次总线读写;
- sequence:一组 item 的编排,可以顺序、并行、循环、条件分支。
二、Sequence 的基本组织方式
2.1 顺序组织
最基础的方式,一个接一个发:
task body(); `uvm_do(req1) `uvm_do(req2) `uvm_do(req3) endtaskuvm_do是宏,等价于:
start_item(req); req.randomize(); finish_item(req);2.2 循环组织
task body(); repeat (10) `uvm_do(req) endtask2.3 并行组织
用fork...join同时发多个 sequence:
task body(); fork `uvm_do(seq_a) `uvm_do(seq_b) join endtask2.4 条件组织
task body(); if (mode == READ) `uvm_do(read_seq) else `uvm_do(write_seq) endtask三、分层 Sequence 组织(Hierarchical Sequences)
这是 UVM 最核心的组织思想:sequence 可以嵌套 sequence。
3.1 基本 sequence 与层次 sequence
- 基本 sequence(leaf sequence):直接产生 item,负责具体事务;
- 层次 sequence(hierarchical sequence):不直接产生 item,而是启动其它 sequence。
例如:
// 叶子 sequence:产生一次读事务 class read_seq extends uvm_sequence #(my_item); `uvm_object_utils(read_seq) task body(); my_item req; `uvm_create(req) req.kind = READ; `uvm_send(req) endtask endclass // 层次 sequence:启动多个叶子 sequence class rw_seq extends uvm_sequence #(my_item); `uvm_object_utils(rw_seq) read_seq r_seq; write_seq w_seq; task body(); `uvm_do(r_seq) `uvm_do(w_seq) `uvm_do(r_seq) endtask endclass这样组织的好处:
- 叶子 sequence 可复用;
- 层次 sequence 只负责编排,不关心细节;
- 修改某一类事务时,只改叶子 sequence。
3.2 层次结构示意
virtual_sequence ├── rw_seq │ ├── read_seq │ └── write_seq ├── config_seq └── error_seq └── error_item四、Virtual Sequence 与 Virtual Sequencer
4.1 为什么需要 virtual sequence
当平台有多个 agent(比如 AXI agent、APB agent),需要一个“总调度”来协调它们。这个总调度就是virtual sequence。
特点:
- virtual sequence不直接产生 item;
- 它内部持有多个 sequencer 的句柄;
- 通过启动不同 agent 的 sequence 来协调多接口激励。
4.2 virtual sequencer
class my_virtual_sequencer extends uvm_sequencer; `uvm_component_utils(my_virtual_sequencer) axi_sequencer axi_sqr; apb_sequencer apb_sqr; function new(string name, uvm_component parent); super.new(name, parent); endfunction endclass4.3 virtual sequence
class my_virtual_seq extends uvm_sequence; `uvm_object_utils(my_virtual_seq) `uvm_declare_p_sequencer(my_virtual_sequencer) axi_read_seq axi_seq; apb_write_seq apb_seq; task body(); fork `uvm_do_on(axi_seq, p_sequencer.axi_sqr) `uvm_do_on(apb_seq, p_sequencer.apb_sqr) join endtask endclassuvm_declare_p_sequencer宏把p_sequencer声明成指定类型,方便访问其中的 sequencer。
4.4 使用方式
task run_phase(uvm_phase phase); phase.raise_objection(this); my_virtual_seq vseq = my_virtual_seq::type_id::create("vseq"); vseq.start(env.v_sqr); // 注意:start 的是 virtual sequencer phase.drop_objection(this); endtask五、Sequence 的启动方式
5.1start()方法
seq.start(sequencer);完整原型:
virtual task start( uvm_sequencer_base sequencer, uvm_sequence_base parent_sequence = null, int this_priority = -1, bit call_pre_post = 1 );sequencer:挂在哪个 sequencer 上;parent_sequence:父 sequence,用于层次管理;this_priority:优先级;call_pre_post:是否调用pre_body/post_body。
5.2uvm_do系列宏
| 宏 | 含义 |
|---|---|
uvm_do(seq) | 创建、随机、启动 |
uvm_do_on(seq, sqr) | 在指定 sequencer 上启动 |
uvm_do_with(seq, {constraints}) | 带约束启动 |
uvm_do_on_with(seq, sqr, {constraints}) | 指定 sequencer 且带约束 |
uvm_create(seq) | 只创建,不启动 |
uvm_send(seq) | 只发送已创建的 seq |
uvm_rand_send(seq) | 随机后发送 |
5.3 手动启动
start_item(req); if (!req.randomize() with { addr < 32'h1000; }) `uvm_error("SEQ", "rand failed") finish_item(req);六、Sequence 的仲裁机制
当多个 sequence 同时挂在同一个 sequencer 上,sequencer 需要决定谁先发。这就是仲裁(arbitration)。
6.1 默认仲裁:FIFO
默认按请求到达顺序,先到先服务。
6.2 优先级仲裁
seq.start(sqr, null, 100); // 优先级 100优先级高的 sequence 优先被处理。
6.3 锁与抢占
lock():锁定 sequencer,别人不能插入;grab():立即抢占,优先级高于 lock;unlock()/ungrab():释放。
seq.lock(sqr); // 独占执行 seq.unlock(sqr);区别:
lock是排队等待,等到位置后独占;grab是立即抢占,插队。
七、Sequence 的层次与生命周期
7.1 父子关系
start()时可以指定父 sequence:
child_seq.start(sqr, parent_seq);父子关系用于:
- 生命周期管理;
- kill 时联动;
- 层次调试。
7.2 pre_body / post_body / body
task pre_body(); endtask // body 之前 task body(); endtask // 主体 task post_body(); endtask // body 之后pre_body和post_body只在call_pre_post = 1且没有 parent sequence 时调用。
7.3 response 处理
sequence 可以通过get_response拿到 driver 返回的 response:
task body(); my_item req, rsp; `uvm_do(req) get_response(rsp); `uvm_info("SEQ", $sformatf("rdata=%0h", rsp.rdata), UVM_LOW) endtask八、Sequence 库(Sequence Library)
当需要随机选择不同 sequence 时,可以用uvm_sequence_library。
class my_seq_lib extends uvm_sequence_library #(my_item); `uvm_object_utils(my_seq_lib) `uvm_sequence_library_utils(my_seq_lib) function new(string name = "my_seq_lib"); super.new(name); init_sequence_library(); endfunction virtual function void init_sequence_library(); add_sequence(read_seq::get_type()); add_sequence(write_seq::get_type()); add_sequence(rw_seq::get_type()); endfunction endclass使用:
my_seq_lib lib; lib = my_seq_lib::type_id::create("lib"); lib.start(sqr);sequence library 会按配置随机选择其中一个 sequence 执行。
九、Sequence 的复用与配置
9.1 通过 factory override 复用
read_seq::type_id::set_type_override(read_seq_ext::get_type());这样调用read_seq::type_id::create()时,实际创建的是read_seq_ext。
9.2 通过 config_db 配置
uvm_config_db#(int)::set(this, "seq", "num_items", 20);sequence 里:
uvm_config_db#(int)::get(null, get_full_name(), "num_items", num_items);9.3 通过uvm_do_with传约束
`uvm_do_with(req, { addr inside {[0:255]}; })十、推荐的 Sequence 组织规范
| 规范 | 说明 |
|---|---|
| 叶子 sequence 只产生 item | 不要嵌套其它 sequence |
| 层次 sequence 只编排 | 不要直接产生 item |
| virtual sequence 放 test 层 | 协调多 agent |
用type_id::create创建 | 支持 factory override |
用uvm_do_on指定 sequencer | 避免句柄混乱 |
| 约束尽量写在 sequence 里 | item 保持通用 |
| 每个 sequence 只做一件事 | 便于复用和调试 |
十一、完整示例
// item class my_item extends uvm_sequence_item; rand bit [31:0] addr; rand bit [31:0] data; rand bit kind; `uvm_object_utils(my_item) endclass // 叶子 sequence class read_seq extends uvm_sequence #(my_item); `uvm_object_utils(read_seq) task body(); my_item req; `uvm_create(req) req.kind = 0; `uvm_send(req) endtask endclass // 层次 sequence class rw_seq extends uvm_sequence #(my_item); `uvm_object_utils(rw_seq) read_seq r; write_seq w; task body(); `uvm_do(r) `uvm_do(w) `uvm_do(r) endtask endclass // virtual sequence class top_vseq extends uvm_sequence; `uvm_object_utils(top_vseq) `uvm_declare_p_sequencer(my_virtual_sequencer) rw_seq rw; task body(); `uvm_do_on(rw, p_sequencer.axi_sqr) endtask endclass // test 里启动 task run_phase(uvm_phase phase); phase.raise_objection(this); top_vseq vseq = top_vseq::type_id::create("vseq"); vseq.start(env.v_sqr); phase.drop_objection(this); endtask十二、总结
UVM sequence 的组织可以概括为四层:
| 层次 | 角色 | 职责 |
|---|---|---|
| item | 最小事务 | 描述一次传输 |
| 叶子 sequence | 基本激励 | 产生 item |
| 层次 sequence | 激励编排 | 组合叶子 sequence |
| virtual sequence | 全局调度 | 协调多 agent |
核心思想:
- 分层:让复杂激励拆成可复用的小块;
- 解耦:sequence 不关心 driver 怎么驱动,只关心发什么;
- 复用:通过 factory、约束、配置实现激励的灵活组合;
- 协调:virtual sequence 负责多接口同步。
一句话概括:
UVM 的 sequence 组织是一个从 item 到叶子 sequence、到层次 sequence、再到 virtual sequence 的分层结构,通过分层实现激励的复用、编排和多接口协调。