UVM Example Reference 1
UVM Example Reference 1
uvm_ref_flow_1.2是不错的学习材料,从UT到SOC的环境都可以参考这里面的代码来写,至少可以当做代码的模板来用。
和我之前一般看到的做法不同,monitor被完全实现在了transaction level,signal level的东西放在了一个叫collector的class里面。
还有一个特别值得说明是,在apb_slave_seq_lib.sv中的simple_response_seq通过其p_sequencer的tlm接口addr_trans_port来生成包,不错的应用方法。
class simple_response_seq extends uvm_sequence #(apb_transfer);
// ...
virtual task body();
`uvm_info(get_type_name(), "Starting...", UVM_MEDIUM)
forever begin
p_sequencer.addr_trans_port.peek(util_transfer);
// ...
end
endtask : body
endclass : simple_response_seq
为了让sequencer能看到发包的情况,建立了一条tlm通道。从总线到apb_collector,再到apb_monitor,最后到apb_slave_sequencer。
<img src="../../images/uvm_ref_flow1.png" class="img-thumbnail" width="60%" >
用事件来在同一个class的两个task之间做同步和信息传递,用peek的tlm通路来层层传递。
class apb_slave_sequencer extends uvm_sequencer #(apb_transfer);
// ...
uvm_blocking_peek_port#(apb_transfer) addr_trans_port;
// ...
endclass : apb_slave_sequencer
class apb_monitor extends uvm_monitor;
// ...
// Allows the sequencer to look at monitored data for responses
uvm_blocking_peek_imp#(apb_transfer,apb_monitor) addr_trans_export;
// Allows monitor to look at collector for address information
uvm_blocking_peek_port#(apb_transfer) addr_trans_port;
event trans_addr_grabbed;
endclass : apb_monitor
// UVM run_phase
task apb_monitor::run_phase(uvm_phase phase);
forever begin
addr_trans_port.peek(trans_collected);
`uvm_info(get_type_name(), $sformatf("Address Phase Complete: %h[%s]", trans_collected.addr, trans_collected.direction.name() ), UVM_HIGH)
-> trans_addr_grabbed;
end
endtask : run_phase
// FUNCTION: peek - Allows the sequencer to peek at monitor for responses
task apb_monitor::peek(output apb_transfer trans);
@trans_addr_grabbed;
trans = trans_collected;
endtask : peek
class apb_collector extends uvm_component;
// ...
// TLM Port - Allows sequencer access to transfer during address phase
uvm_blocking_peek_imp#(apb_transfer,apb_collector) addr_trans_export;
event addr_trans_grabbed;
endclass : apb_collector
// UVM run_phase()
task apb_collector::run_phase(uvm_phase phase);
@(posedge vif.preset);
`uvm_info(get_type_name(), "Detected Reset Done", UVM_LOW)
collect_transactions();
endtask : run_phase
// collect_transactions
task apb_collector::collect_transactions();
forever begin
@(posedge vif.pclock iff (vif.psel != 0));
void'(this.begin_tr(trans_collected,"APB_COLLECTOR","UVM Debug","APB collector transaction inside collect_transactions()"));
trans_collected.addr = vif.paddr;
trans_collected.master = cfg.master_config.name;
trans_collected.slave = cfg.get_slave_name_by_addr(trans_collected.addr);
case (vif.prwd)
1'b0 : trans_collected.direction = APB_READ;
1'b1 : trans_collected.direction = APB_WRITE;
endcase
@(posedge vif.pclock);
if(trans_collected.direction == APB_READ)
trans_collected.data = vif.prdata;
if (trans_collected.direction == APB_WRITE)
trans_collected.data = vif.pwdata;
-> addr_trans_grabbed;
@(posedge vif.pclock);
if(trans_collected.direction == APB_READ) begin
if(vif.pready != 1'b1)
@(posedge vif.pclock);
trans_collected.data = vif.prdata;
end
this.end_tr(trans_collected);
item_collected_port.write(trans_collected);
`uvm_info(get_type_name(), $sformatf("Transfer collected :\n%s",
trans_collected.sprint()), UVM_MEDIUM)
`ifdef HEAP
runq.push_back(trans_collected);
`endif
num_transactions++;
end
endtask : collect_transactions
task apb_collector::peek(output apb_transfer trans);
@addr_trans_grabbed;
trans = trans_collected;
endtask : peek
再看一下连接关系。
// UVM connect_phase
function void apb_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
// Get the virtual interface if set via get_config
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_error("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"})
bus_collector.item_collected_port.connect(bus_monitor.coll_mon_port);
bus_monitor.addr_trans_port.connect(bus_collector.addr_trans_export);
master.monitor = bus_monitor;
master.collector = bus_collector;
foreach(slaves[i]) begin
slaves[i].monitor = bus_monitor;
slaves[i].collector = bus_collector;
if (slaves[i].is_active == UVM_ACTIVE)
slaves[i].sequencer.addr_trans_port.connect(bus_monitor.addr_trans_export);
end
endfunction : connect_phase