UVM sequence-driver 通信模型(详细讲解)
核心一句话:sequence 负责产生激励事务(item),driver 负责把事务变成硬件信号打给DUT;两者通过
uvm_seq_item_pull_port/export握手通信 ,属于pull模型(driver主动拉取item),这是UVM默认标准模型。
一、整体角色分工
- sequence(uvm_sequence) :激励发生器,写在
body()里,创建seq_item,把item交给sequencer。不直接访问DUT信号 - sequencer(uvm_sequencer) :中间"快递中转站"。有FIFO缓存,接收sequence发来的item,等待driver来取。
- 端口:
uvm_seq_item_pull_imp #(TR, this_type) seq_item_export;
- 端口:
- driver(uvm_driver) :从sequencer拿transaction,解析item,驱动接口信号到DUT。
- 端口:
uvm_seq_item_pull_port #(TR) seq_item_port;
- 端口:
- 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,对应两个握手动作:
get_next_item(req):driver向sequencer申请拿新item(阻塞)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 :调用
get_next_item,忘记写item_done()
→ sequencer永远等待ack,sequence卡在finish_item,仿真卡死。 - 死锁场景2 :多次连续调用
get_next_item,中间没有item_done
→ 一次只能持有1个transaction,不支持预取多个。 - sequence不调用
start_item/finish_item,直接seq_item_port.put
→ put是push模型(sequence主动推item),不是UVM默认pull模型,很少用。 - 端口方向搞反:
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被动接收,很少在激励通路使用。
七、面试常问总结
- sequence和driver不直接相连,中间必须经过sequencer
- 通信端口是
uvm_seq_item_pull_port,标准是pull模型 - 标准两步握手:
get_next_item()拿item → 驱动DUT →item_done()确认完成 - sequencer内置FIFO缓存transaction,可以支持多个sequence并发(virtual sequence)