UVM 层次化sequence构建方式(layering sequence)
文章目录
layering sequence介绍
- 如果我们在构建更加复杂的协议总线传输,例如PCIe,USB3.0等,那么通过一个单一的传输层级会对以后的激励复用、上层控制不那么友好。对于这种更深层次化的数据传输,在实际中无论是VIP还是自开发的环境,都倾向于通过若干抽象层次的sequence群落来模拟协议层次。
- 通过层次化的sequence可以分别构建transaction layer、transport layer和physical layer等从高抽象级到低抽象级的transaction转换。这种层次化的sequence构建方式,我们称之为layering sequence。例如在进行寄存器级别的访问操作,其需要通过transport layer转换,最终映射为具体的总线传输。
- 我们将通过一端简单的例码,来阐述layer sequence的核心,突出sequence层级转换的思想。
layering sequence示例
typedef enum{CLKON,CLKOFF,RESET,WRREG,RDREG} phy_cmd_t;
typedef enum{FREG_LOW_TRANS,FREG_MED_TRANS,FREG_HIGH_TRANS}layer_cmd_t;
class bus_trans extends uvm_sequence_item;
rand phy_cmd_t cmd;
rand int addr;
rand int data;
constraint cstr{
soft addr=='h0;
soft data=='h0;
};
...
endclass
class packet_seq extends uvm_sequence;
rand int len;
rand int addr;
rand int data[];
rand phy_cmd_t cmd;
constraint cstr{
soft len inside {[30:50]};
soft addr[31:26]=='hFF00;
data.size()==len;
};
...
task body();
bus_trans req;
foreach(data[i])
`uvm_do_with{req,{cmd==local::cmd;
addr==local::addr;
data==local::data[i];}}
endtask
endclass
class layer_trans extends uvm_sequence_item;
rand layer_cmd_t layer_cmd;
rand int pkt_len;
rand int pkt_idle;
constraint cstr{
soft pkt_len inside{[10:20]};
layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]};
layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]};
layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};
};
...
endclass
class adapter_sequence extends uvm_sequence;
`uvm_object_utils(adapter_seq)
`uvm_declare_p_sequencer(phy_master_sequencer)
...
task body();
layer_trans trans;
packet_seq pkt;
forever begin
p_sequencer.up_sqr.get_next_item(req);
void'($cast(trans,req));
repeat(trans.pkt_len)begin
`uvm_do(pkt)
delay(trans.pkt_idle);
end
p_sequencer.up_sqr.item_done();
end
endtask
virtual task delay(int delay);
...
endtask
endclass
class top_seq extends uvm_sequence;
...
task body();
layer_trans trans;
`uvm_do_with(trans,{layer_cmd == FREQ_LOW_TRANS;})
`uvm_do_with(trans,{layer_cmd == FREQ_HIGH_TRANS;})
endtask
endclass
class layering_sequencer extends uvm_sequencer;
...
endclass
class phy_master_sequencer extends uvm_sequencer;
layering_sequencer up_sqr;
...
endclasss
class phy_master_driver extends uvm_driver;
...
task run_phase(uvm_phase phase);
REQ tmp;
bus_trans req;
forever begin
seq_item_port.get_next_item(tmp);
void'($cast(req,tmp));
`uvm_info("DRV",$sformatf("got a item \n %s",req.sprint()),UVM_LOW)
seq_item_port.item_done();
end
endtask
endclass
class phy_master_agent extends uvm_agent;
phy_master_driver drv;
phy_master_sequencer sqr;
...
function void build_phase(uvm_phase phase);
sqr=reg_master_sequencer::type_id::create("sqr",this);
drv=reg_master_driver::type_id::create("drv",this);
endfunction
function void connect_phase(uvm_phase phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
class test1 extends uvm_test;
layering_sequencer layer_sqr;
phy_master_agent phy_agt;
...
function void build_phae(uvm_phase phase);
layer_sqr=layering_sequencer::type_id::create("layer_sqr",this);
phy_agt=phy_master_agent::type_id::create("phy_agt",this);
endfunction
function void connect_phase(uvm_phase phase);
phy_agt.sqr.up_sqr=layer_sqr;
endfunction
task run_phase(uvm_phase phase);
top_seq seq;
adapter_seq adapter;
phase.raise_objection(phase);
seq=new();
adapter=new();
fork
adapter.start(phy_agt.sqr);
join_none
seq.start(layer_sqr);
phase.drop_objection(phase);
endtask
endclass
不是driver,可以调用get_next_item吗?也可以,因为get_next_item方法实际上是属于sqr的,那为什么driver可以直接通过port调用该方法呢?这是因为port与sqr的import相连。
我们可以得出一些关于如何实现sequencer layer协议转换的方法:
- 无论有多少抽象层次的transaction类定义,都应该有对应的sequencer作为transaction的路由通道。例如layer_sequencer和phy_master_sequencer分别作为layer_trans和bus_trans的通道。
- 在各个抽象级的sequence中,需要有相应的转换方法,将从高层次的transaction从高层次的sequencer获取,继而转换为低层次的transaction,最终通过低层次的sequencer发送出去。例如adapter_seq负责从layer_sequencer获取layer_trans,再将其转换为phy_master_sequencer一侧对应的sequence或者transaction,最后再将其从phy_master_sequencer发送出去。
- 这些adaption sequence应该运行再低层次的sequencer一侧,作为"永动"的sequence时刻做好服务准备,从高层次的sequencer获取transaction,通过转换将其从低层次的sequencer一侧送出。例如上面在test1中,adapter sequence通过adapter.start(phy_agt.sqr)挂载到低层次的sequencer,做好转换transaction并将其发送的准备。
- 至于需要定义多少个层次的transaction item类,上面的例子仅仅是为了说明layer sequence的一般方法,对于实际中的层次定义和transaction item定义,我们还需要具体问题具体处理。
layering sequence解析
我们可以看到各个sequence类别对应的sequencer,同时也有sequence item发送和转换的方法。经过转换,最终高层次的transaction内容可以落实到低层次的protocol agent和physical interface上面。
例码中没有给出读回路的处理,即从physical interface穿过physical agent,最终抵达layering sequencer的通信。在实际中我们可以通过monitor转换抽象级返回item的方式来实现。
也可以通过不同层次的monitor采集response transcation,最终通过monitor转换抽象级返回item的方式来实现。
至于选择哪一种反馈回路,这与底层agent反馈回路的实现方式有关,即如果原有方法通过driver一侧返回response,那么我们建议继续在该反馈链条上进行从低级transaction到高级transaction的转换,如果原有方式通过monitor一侧返回response,那么我们也建议创建对应的高层次monitor,抽线抽象层次转换。
关注作者
- 自述
作者是一位中科大数字设计专业的研究生,水平有限,如有错误,请大家指正,想要与大家一同进步。 - 经历
曾获得国家奖学金,“高教社杯”数学建模国家二等奖等 - 陆续更新:
1.与UVM验证相关的system verilog后续内容;
2.与verilog数字设计相关的一些基础模块设计,例如FIFO,UART,I2C等的书写。
3.保研与竞赛经历等 - 微信公众号
欢迎大家关注公众号“数字IC小白的日常修炼”,期待与大家一同仗剑遨游数字IC世界。