UVM 序列(Sequence)组织详解
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 一个最简单的 sequenceclass 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同时发多个 sequencetask 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基本 sequenceleaf sequence直接产生 item负责具体事务层次 sequencehierarchical 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 Sequencer4.1 为什么需要 virtual sequence当平台有多个 agent比如 AXI agent、APB agent需要一个“总调度”来协调它们。这个总调度就是virtual sequence。特点virtual sequence不直接产生 item它内部持有多个 sequencer 的句柄通过启动不同 agent 的 sequence 来协调多接口激励。4.2 virtual sequencerclass 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 sequenceclass 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)只发送已创建的 sequvm_rand_send(seq)随机后发送5.3 手动启动start_item(req); if (!req.randomize() with { addr 32h1000; }) 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()立即抢占优先级高于 lockunlock()/ungrab()释放。seq.lock(sqr); // 独占执行 seq.unlock(sqr);区别lock是排队等待等到位置后独占grab是立即抢占插队。七、Sequence 的层次与生命周期7.1 父子关系start()时可以指定父 sequencechild_seq.start(sqr, parent_seq);父子关系用于生命周期管理kill 时联动层次调试。7.2 pre_body / post_body / bodytask 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 返回的 responsetask 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 只编排不要直接产生 itemvirtual 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激励编排组合叶子 sequencevirtual sequence全局调度协调多 agent核心思想分层让复杂激励拆成可复用的小块解耦sequence 不关心 driver 怎么驱动只关心发什么复用通过 factory、约束、配置实现激励的灵活组合协调virtual sequence 负责多接口同步。一句话概括UVM 的 sequence 组织是一个从 item 到叶子 sequence、到层次 sequence、再到 virtual sequence 的分层结构通过分层实现激励的复用、编排和多接口协调。