| |
Connecting the testbench to the DUT
使用抽象类和具体类的测试平台与DUT的连接类似于使用虚拟接口来进行连接的情况。通过虚拟接口连接,虚拟接口句柄放置在事务传输器(例如driver或monitor)内部,并使该句柄指向连接到DUT的接口的实例【也即是等价于将一层层传下来的实际物理接口传给driver或monitor中的虚拟接口句柄】。在抽象/具体类方法中,抽象类类型的句柄放在transactor中。具体类的实例化在包装器模块(wb_bus_protocol_module)内部进行,且抽象类句柄指向具体类的实例。
【此处需一幅图】
在上图中,DUT和wb_bus_protocol_module包装在包装模块wb_bus_wrapper之中。这是为了实现多个wishbone buses的模块化和便利性。
下面的代码显示了包装器模块中的具体类的实例,以及用于创建具体类实例的method(“延迟分配器”,即在需要之前不分配实例的method)。
module wb_bus_protocol_module #(int WB_ID = 0, int num_masters = 8, int num_slaves = 8,
int data_width = 32, int addr_width = 32)
(
// Port declarations
// WISHBONE common signals
output logic clk,
output logic rst,
...
);
// Concrete class declaration
class wb_bus_concr_c #(int ID = WB_ID) extends wb_bus_abs_c;
...
endclass
// instance of concrete class
wb_bus_concr_c wb_bus_concr_c_inst;
// lazy allocation of concrete class
function wb_bus_abs_c get_wb_bus_concr_c_inst();
if(wb_bus_concr_c_inst == null)
wb_bus_concr_c_inst = new();
return (wb_bus_concr_c_inst);
endfunction
initial
//set concrete class object in config space
uvm_config_db #(wb_bus_abs_c)::set(null, "uvm_test_top",
$sformatf("WB_BUS_CONCR_INST_%0d",WB_ID),
get_wb_bus_concr_c_inst());
// WISHBONE BFM instance
wishbone_bus_syscon_bfm wb_bfm(
.clk( clk ),
.rst( rst ),
...
);
endmodule
具体类实例的位置以与虚拟接口连接相同的方式提供给transactor,使用config_db API将指向具体类实例的句柄传递给test class,然后通过配置对象从test class传递到transactor。在上面的代码中,在初始块内部,使用config_db API将具体实例的句柄放在配置空间内。
在test class中,concrete driver class实例的句柄从配置数据库中获取,并放置在可供wishbone agent使用的配置对象中。建议使用此方法,因为它遵循推荐的使用模型,用于将信息从DUT传递到测试平台,这将在有关虚拟接口的文章中详细讨论。
class test_mac_simple_duplex extends uvm_test;
...
mac_env env_0;
wb_config wb_config_0; // config object for WISHBONE BUS 0
...
function void set_wishbone_config_params();
wb_config_0 = new();
if (!uvm_config_db #(wb_bus_bfm_driver_base)::get(this, "",
$sformatf("WB_BUS_BFM_DRIVER_C_INST_%0h", wb_config_0.m_wb_id),
wb_config_0.m_wb_bfm_driver)) // concrete class object
`uvm_fatal(report_id, "Can't read WB_BUS_BFM_DRIVER_C_INST");
...
uvm_config_db#(wb_config)::set(this, "env_0*", "wb_config", wb_config_0); // put in config
...
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
set_wishbone_config_params();
set_mii_config_params();
...
endfunction : build_phase
...
endclass
在driver内部,通过从test class提供的配置对象中获取位置,使抽象类句柄指向具体类的实例。
// WISHBONE master driver
class wb_bus_bfm_driver extends uvm_driver #(wb_txn, wb_txn);
`uvm_component_utils(wb_bus_bfm_driver)
uvm_analysis_port #(wb_txn) wb_drv_ap;
bit [2:0] m_id; // Wishbone bus master ID
wb_config m_config;
wb_bus_abs_c m_wb_bus_abs_c;
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db #(wb_config)::get(this, "", "wb_config", m_config)) // get config object
`uvm_fatal("Config Fatal", "Can't get the wb_config")
m_id = m_config.m_wb_master_id;
wb_drv_ap = new("wb_drv_ap", this);
// Assign abstract class handle to concrete object
m_wb_bus_abs_c = m_config.m_wb_bus_abs_c;
endfunction : build_phase
task run_phase(uvm_phase phase);
wb_txn req_txn;
forever begin
seq_item_port.get(req_txn); // get transaction
@ ( m_wb_bus_abs_c.pos_edge_clk) #1; // sync to clock edge + 1 time step
case(req_txn.txn_type) //what type of transaction?
NONE: `uvm_info($sformatf("WB_M_DRVR_%0d",m_id),
$sformatf("wb_txn %0d the wb_txn_type was type NONE",
req_txn.get_transaction_id()), UVM_LOW)
WRITE: wb_write_cycle(req_txn);
READ: wb_read_cycle(req_txn);
RMW: wb_rmw_cycle(req_txn);
WAIT_IRQ: fork wb_irq(req_txn); join_none
default: `uvm_error($sformatf("WB_M_DRVR_%0d",m_id),
$sformatf("wb_txn %0d the wb_txn_type was type illegal",
req_txn.get_transaction_id()) )
endcase
end
endtask : run_phase
//READ 1 or more cycles
virtual task wb_read_cycle(wb_txn req_txn);
wb_txn rsp_txn;
m_wb_bus_abs_c.wb_read_cycle(req_txn, m_id, rsp_txn);
seq_item_port.put(rsp_txn); // send rsp object back to sequence
wb_drv_ap.write(rsp_txn); //broadcast read transaction with results
endtask
//WRITE 1 or more write cycles
virtual task wb_write_cycle(wb_txn req_txn);
wb_txn orig_req_txn;
$cast(orig_req_txn, req_txn.clone()); //save off copy of original req transaction
m_wb_bus_abs_c.wb_write_cycle(req_txn, m_id);
wb_drv_ap.write(orig_req_txn); //broadcast orignal transaction
endtask
//RMW ( read-modify_write)
virtual task wb_rmw_cycle(ref wb_txn req_txn);
`uvm_info($sformatf("WB_M_DRVR_%0d",m_id),
"Wishbone RMW instruction not implemented yet",UVM_LOW )
endtask
// wait for an interrupt
virtual task wb_irq(wb_txn req_txn);
wb_txn rsp_txn;
m_wb_bus_abs_c.wb_irq(req_txn, rsp_txn);
seq_item_port.put(rsp_txn); // send rsp object back to sequence
endtask
endclass
当driver在运行任务中收到WISHBONE写事务时,它调用其wb_write_cycle()task,该task使用抽象类句柄(m_wb_bus_abs_c)来调用具体类中的wb_write_cycle()method,该method又调用BFM之中的wb_write_cycle()method。
Multiple instance considerations
当存在DUT和BFM的多个实例时,每个具体类的实例需要“不加规划”,以便可以进行正确的配对。做这个的一个方法是对包装类和具体类进行参数化。在此示例中,提供了一个整数值,即WISHBONE总线ID(WB_ID),以生成唯一的字符串。这样做是为了在总线id和具体类之间建立相关性。有关使具体类实例唯一的另一种方法在协议模块的文章中有所描述(参见底部)。本文中的完整源代码实际上在top_mac中有两个wishbone包装器类实例,而不是图中所示的实例,因此有两个WISHBONE总线,MAC等。在测试平台上,每个WISHBONE总线都有一个wishbone环境,而不是图中所示的仅仅一个。为每个wishbone环境生成单独的配置对象。本文未显示多个实例的详细信息,但可以在源代码中查看。
(在http://verificationacademy.com/uvm-ovm上在线下载源代码示例)。