UVM sequence-driver 通信模型(详细讲解)

UVM sequence-driver 通信模型(详细讲解)

核心一句话:sequence 负责产生激励事务(item),driver 负责把事务变成硬件信号打给DUT;两者通过 uvm_seq_item_pull_port/export 握手通信 ,属于pull模型(driver主动拉取item),这是UVM默认标准模型。

一、整体角色分工

  1. sequence(uvm_sequence) :激励发生器,写在body()里,创建seq_item,把item交给sequencer。不直接访问DUT信号
  2. sequencer(uvm_sequencer) :中间"快递中转站"。有FIFO缓存,接收sequence发来的item,等待driver来取。
    • 端口:uvm_seq_item_pull_imp #(TR, this_type) seq_item_export;
  3. driver(uvm_driver) :从sequencer拿transaction,解析item,驱动接口信号到DUT。
    • 端口:uvm_seq_item_pull_port #(TR) seq_item_port;
  4. seq_item(uvm_sequence_item) :事务数据包,存放激励数据、控制位(addr/data/wr_en等),纯数据载体

UVM树简易结构(Mermaid)

#mermaid-svg-7UgcXUWoWJrIbcnO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7UgcXUWoWJrIbcnO .error-icon{fill:#552222;}#mermaid-svg-7UgcXUWoWJrIbcnO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7UgcXUWoWJrIbcnO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7UgcXUWoWJrIbcnO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7UgcXUWoWJrIbcnO .marker.cross{stroke:#333333;}#mermaid-svg-7UgcXUWoWJrIbcnO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7UgcXUWoWJrIbcnO p{margin:0;}#mermaid-svg-7UgcXUWoWJrIbcnO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7UgcXUWoWJrIbcnO .cluster-label text{fill:#333;}#mermaid-svg-7UgcXUWoWJrIbcnO .cluster-label span{color:#333;}#mermaid-svg-7UgcXUWoWJrIbcnO .cluster-label span p{background-color:transparent;}#mermaid-svg-7UgcXUWoWJrIbcnO .label text,#mermaid-svg-7UgcXUWoWJrIbcnO span{fill:#333;color:#333;}#mermaid-svg-7UgcXUWoWJrIbcnO .node rect,#mermaid-svg-7UgcXUWoWJrIbcnO .node circle,#mermaid-svg-7UgcXUWoWJrIbcnO .node ellipse,#mermaid-svg-7UgcXUWoWJrIbcnO .node polygon,#mermaid-svg-7UgcXUWoWJrIbcnO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7UgcXUWoWJrIbcnO .rough-node .label text,#mermaid-svg-7UgcXUWoWJrIbcnO .node .label text,#mermaid-svg-7UgcXUWoWJrIbcnO .image-shape .label,#mermaid-svg-7UgcXUWoWJrIbcnO .icon-shape .label{text-anchor:middle;}#mermaid-svg-7UgcXUWoWJrIbcnO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7UgcXUWoWJrIbcnO .rough-node .label,#mermaid-svg-7UgcXUWoWJrIbcnO .node .label,#mermaid-svg-7UgcXUWoWJrIbcnO .image-shape .label,#mermaid-svg-7UgcXUWoWJrIbcnO .icon-shape .label{text-align:center;}#mermaid-svg-7UgcXUWoWJrIbcnO .node.clickable{cursor:pointer;}#mermaid-svg-7UgcXUWoWJrIbcnO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7UgcXUWoWJrIbcnO .arrowheadPath{fill:#333333;}#mermaid-svg-7UgcXUWoWJrIbcnO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7UgcXUWoWJrIbcnO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7UgcXUWoWJrIbcnO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7UgcXUWoWJrIbcnO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7UgcXUWoWJrIbcnO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7UgcXUWoWJrIbcnO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7UgcXUWoWJrIbcnO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7UgcXUWoWJrIbcnO .cluster text{fill:#333;}#mermaid-svg-7UgcXUWoWJrIbcnO .cluster span{color:#333;}#mermaid-svg-7UgcXUWoWJrIbcnO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7UgcXUWoWJrIbcnO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7UgcXUWoWJrIbcnO rect.text{fill:none;stroke-width:0;}#mermaid-svg-7UgcXUWoWJrIbcnO .icon-shape,#mermaid-svg-7UgcXUWoWJrIbcnO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7UgcXUWoWJrIbcnO .icon-shape p,#mermaid-svg-7UgcXUWoWJrIbcnO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7UgcXUWoWJrIbcnO .icon-shape .label rect,#mermaid-svg-7UgcXUWoWJrIbcnO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7UgcXUWoWJrIbcnO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7UgcXUWoWJrIbcnO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7UgcXUWoWJrIbcnO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} seq_item_port ↔ seq_item_export
uvm_test_top: test_case
my_sequencer
my_driver
my_sequence
virtual interface -> DUT

连接关系:driver.seq_item_port.connect(sequencer.seq_item_export);

sequence 挂载到 sequencer:seq.start(seqr);

二、通信端口类型:pull端口(重点!)

uvm_seq_item_pull_port 包含两组API,对应两个握手动作:

  1. get_next_item(req):driver向sequencer申请拿新item(阻塞)
  2. item_done():driver告知sequencer当前item传输完成

✅ 这就是经典 2步握手,是sequence-driver最核心通信机制。

完整通信时序(标准2步握手流程)

复制代码
1. sequence 执行 `start_item(item)` 
   → 请求sequencer分配权限,阻塞等待sequencer空闲
2. sequence 执行 `finish_item(item)`
   → 将item发送给sequencer的内部FIFO,等待driver取走
===== 上面是sequence侧;下面driver侧循环 =====
3. driver 中 `seq_item_port.get_next_item(req);` 
   → driver阻塞,去sequencer拉取item。
      如果sequencer还没有item,driver停在这里等(阻塞)
4. driver拿到req(seq_item),解析里面的数据,驱动DUT接口信号
5. 驱动完成后,调用 `seq_item_port.item_done();`
   → 通知sequencer:这个item处理完毕!
   → sequencer收到item_done后,唤醒sequence,sequence继续产生下一笔激励
6. driver回到循环,再次调用 get_next_item,取下一个item,往复循环

⚠️ 关键点:

get_next_item() 和 item_done() 必须成对出现 ,不能丢、不能乱序!

get_next_item拿到item之后,在item_done之前,sequencer不会给driver下一个item

三、代码最简可运行Demo(带逐行注释)

1. 事务 my_item.sv

systemverilog 复制代码
class my_item extends uvm_sequence_item;
  `uvm_object_utils(my_item)
  rand bit [7:0] data;
  function new(string name="my_item");
    super.new(name);
  endfunction
endclass

2. Sequence (产生激励)

systemverilog 复制代码
class my_seq extends uvm_sequence#(my_item);
  `uvm_object_utils(my_seq)
  `uvm_declare_p_sequencer(my_sequencer) //绑定sequencer类型
  function new(string name="my_seq");
    super.new(name);
  endfunction

  virtual task body();
    my_item req;
    repeat(3) begin  //产生3笔transaction
      req = my_item::type_id::create("req");
      start_item(req);  //【1】请求sequencer权限,阻塞
      assert(req.randomize());
      finish_item(req); //【2】把item发给sequencer,等待driver取完+item_done
      `uvm_info("SEQ",$sformatf("send item data = %0d",req.data),UVM_MEDIUM)
    end
  endtask
endclass

3. Sequencer

systemverilog 复制代码
class my_sequencer extends uvm_sequencer#(my_item);
  `uvm_component_utils(my_sequencer)
  function new(string name="my_sequencer",uvm_component parent=null);
    super.new(name,parent);
  endfunction
endclass

4. Driver(从sequencer拉item,驱动DUT)

systemverilog 复制代码
class my_driver extends uvm_driver#(my_item);
  `uvm_component_utils(my_driver)
  virtual my_intf vif;
  function new(string name="my_driver",uvm_component parent=null);
    super.new(name,parent);
  endfunction

  virtual task run_phase(uvm_phase phase);
    my_item req;
    forever begin
      //【3】driver主动拉取item,没有item就阻塞等待
      seq_item_port.get_next_item(req);
      
      // 把item数据驱动到DUT接口
      vif.data <= req.data;
      #10ns;
      `uvm_info("DRV",$sformatf("drive DUT data=%0d",req.data),UVM_MEDIUM)
      
      //【5】通知sequencer:当前item处理完成!
      seq_item_port.item_done();
    end
  endtask
endclass

5. Environment + Test

systemverilog 复制代码
class my_env extends uvm_env;
  `uvm_component_utils(my_env)
  my_sequencer seqr;
  my_driver drv;
  function new(string name="my_env",uvm_component parent=null);
    super.new(name,parent);
  endfunction
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    seqr = my_sequencer::type_id::create("seqr",this);
    drv  = my_driver::type_id::create("drv",this);
  endfunction
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    //【端口连接!】driver端口连sequencer的export
    drv.seq_item_port.connect(seqr.seq_item_export);
    uvm_config_db#(virtual my_intf)::set(this,"drv","vif",top.vif);
  endfunction
endclass

class test1 extends uvm_test;
  `uvm_component_utils(test1)
  my_env env;
  function new(string name="test1",uvm_component parent=null);
    super.new(name,parent);
  endfunction
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = my_env::type_id::create("env",this);
  endfunction
  virtual task run_phase(uvm_phase phase);
    my_seq seq;
    phase.raise_objection(this);
    seq = my_seq::type_id::create("seq");
    seq.start(env.seqr); //sequence挂载sequencer启动
    phase.drop_objection(this);
  endtask
endclass

四、两种握手方式:2步握手 vs 1步get模型

✅ 模型A:2步握手(get_next_item + item_done,最常用)

优点:driver拿到item后可以花费任意长时间 驱动DUT(多周期激励)

缺点:必须成对调用,写错容易卡死仿真(死锁)

✅ 模型B:一步get seq_item_port.get(req)

等价于:get_next_item + 自动内部调用item_done

适合:单周期简单激励,driver拿到item立刻处理完。

限制:拿到item之后不能跨周期驱动,不适合复杂时序激励。

systemverilog 复制代码
seq_item_port.get(req); 
//内部自动完成 item_done,不需要手动写item_done

五、常见坑(面试高频考点)

  1. 死锁场景1 :调用get_next_item,忘记写item_done()
    → sequencer永远等待ack,sequence卡在finish_item,仿真卡死。
  2. 死锁场景2 :多次连续调用get_next_item,中间没有item_done
    → 一次只能持有1个transaction,不支持预取多个。
  3. sequence不调用start_item/finish_item,直接seq_item_port.put
    → put是push模型(sequence主动推item),不是UVM默认pull模型,很少用。
  4. 端口方向搞反:drv.seq_item_export.connect(seqr.seq_item_port) 直接报错 port(请求方driver)连 export(提供方sequencer)

六、Pull模型 vs Push模型(区分)

  • Pull模型(默认,sequence-seqr-driver):Driver主动拉
    Driver是主控,什么时候取激励由driver决定,适合DUT有backpressure(反压)场景,DUT忙的时候driver可以慢一点取item。
  • Push模型:sequence主动put推送给driver
    sequence主控,强制把item塞给driver,driver被动接收,很少在激励通路使用。

七、面试常问总结

  1. sequence和driver不直接相连,中间必须经过sequencer
  2. 通信端口是uvm_seq_item_pull_port,标准是pull模型
  3. 标准两步握手:get_next_item()拿item → 驱动DUT → item_done()确认完成
  4. sequencer内置FIFO缓存transaction,可以支持多个sequence并发(virtual sequence)
相关推荐
博览鸿蒙2 小时前
数字芯片设计实战路线:硬件工程师的 FPGA
fpga开发
zhutier-3 小时前
Vivado+Artix7+W25Q128JVEIQ FPGA固化程序到FLASH里面教程
fpga开发
hexiaoyan8273 小时前
FMC 子卡设计原理图:154-基于FMC 八路SFP+万兆光纤子卡
fpga开发·光纤收发·fmc 子卡·万兆光纤子卡·sfp+万兆光纤
北京青翼科技15 小时前
青翼基于PCIe总线架构的4路HD-SDI/3G-SDI视频图像模拟源丨图像处理
fpga开发·图像模拟源·fpga开发板·pcie板卡
涛tao讲道19 小时前
星载FPGA选型与抗辐射设计手册
fpga开发·抗单粒子翻转·涛tao悟道·涛的天道观·seu
艾芯微科技19 小时前
钰泰 ETA6973PQ3Y|QFN3×3 2A 同步降压 Buck,I2C 可配置多档输出,动态调压 PMIC
网络·单片机·嵌入式硬件·fpga开发·集成测试·51单片机
NamoAmitabha12819 小时前
lwIP 问答---deepseek
fpga开发
吉他英雄一里19 小时前
时钟IP核(MMCM PLL)
fpga开发
星夜离尘5619 小时前
FPGA实现CRC-8/SMBUS:从参数和位序核对到1041次自检
fpga开发·verilog·fpga·crc