UVM验证平台搭建二:apb_agent
一、概述
在搭建UVM验证平台过程中,AMBA总线是会被经常用到的,一般情况下在对寄存器的读写都会用到apb和ahb总线。本文在上章生成的spi_reg_model的基础上,来搭建UVM验证平台的组件apb agent,该组件其实是可以直接调用vip的,而且像apb、ahb这种在不同的项目中,重用性还是很高的,在当前项目中用完,后续项目也可以直接移植该agent到新的项目。下文根据apb总线协议来实现UVM验证平台中的apb agent组件。
二、apb interface
interface apb_if(input bit pclk, input bit presetn);
logic [9:0] paddr;
logic [31:0] prdata;
logic [31:0] pwdata;
logic psel;
logic penable;
logic pwrite;
logic pready;
logic pslverr;
logic [3:0] pstrb;
clocking master_ck @(posedge pclk);
input preadgy;
output pwrite;
output paddr;
output pwdata;
output psel;
output penable;
output pstrb;
output prdata;
endcloking
clocking slave _ck @(posedge pclk);
input pwrite;
input paddr;
input pwdata;
input psel;
input penable;
output prdata;
endclocking
clocking passive_ck @(posedge pclk);
input pwrite;
input paddr;
input pwdata;
input psel;
input penable;
input prdata;
endclocking
modport master_if(clocking master_ck);
modport slave_if(clocking slave_ck);
modport passive_if(clocking passive_ck);
endinterface
三、apb transaction
class apb_trans extends uvm_sequence_item;
typedef enum {READ,WRITE} op_kind;
logic [31:0] data;
rand op_kind,kind;
logic [9:0] addr;
logic [15:0] tdr_data;
logic [15:0] rdr_data;
logic [3:0] pstrb;
`uvm_object_utils_begin(apb_trans)
`uvm_field_int(addr, UVM_ALL_ON);
`uvm_field_int(data, UVM_ALL_ON);
`uvm_field_int(tdr_data, UVM_ALL_ON);
`uvm_field_int_rdr_data, UVM_ALL_ON);
`uvm_field_int(op_kind,kind,UVM_ALL_ON);
`uvm_field_int(pstrb);
`uvm_object_utils_end
function new(string name = "apb_trans");
super.new(name);
endfunctuion
endclass
四、apb sequencer
class apb_sequencer extends uvm_sequencer#(apb_trans);
ral_block_reg_model p_rm;
function new(string name = "apb_sequencer", uvm_component parent);
super.new(name,parent);
endfunction
`uvm_component_utils(apb_sequencer)
endclass
五、apb adapter
class apb_adapt extnds uvm_adapter);
function new(string name = "apb_adapter");
super.new(name);
endfunction
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
apb_trans apb;
apb = apb_trans::type_id::create("apb_trans");
apb.kind = (rw.kind == UVM_READ) ? apb_trans::READ : apb_trans::WRITE;
apb.addr = rw.addr;
apb.data = rw.data;
return apb;
endfunction
virtual function void bus2reg(uvm_sequence_item bus_item,ref uvm_reg_bus_op rw);
apb_trans apb;
if(!$cast(apb,bus_item)) begin
`uvm_fatal("NO APB TYPE","Provided bus_item is not of the correct type!")
return;
end
rw.kind = (apb.kind == apb_trans::READ) ? UVM_READ : UVM_WRITE;
rw.addr = apb.addr;
rw.data = apb.data;
rw.status = UVM_IS_OK;
endfunction
endclass
六、apb_driver
class apb_driver extends uvm_driver#(apb_trans);
`uvm_component_utils(apb_driver)
virtual apb_if bif;
process precesses[string];
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual apb_if)::get(this,"","bif",bif)) begin
`uvm_fatal("apb_driver",No driver intreface specifed for this driver interface!)
end
endfunction
virtual task run_phase(uvm_phase phase);
process proc_apb_driver;
super.run_phase(phase);
this.bif.master_ck.psel <= 'b0;
this.bif.master_ck.penable <= 'b0;
this.bif.master_ck.pwdata <= 32'h0;
this.bif.master_ck.paddr <= 10'h0;
this.bif.master_ck.pwrite <= 'b0;
fork
begin
while(!bif.presetn)
@(posedge bif.pclk);
forever begin
apb_trans tr;
proc_apb_driver = process::self();
processes["proc_apb_driver"] = proc_apb_driver;
@(this.bif.master_ck);
seq_item_port.get_next_item(tr);
case(tr.kind)
apb_trans::READ: this.read(tr.addr, tr.data);
apb_trans::WRITE: this.write(tr.addr, tr.data);
endcase
seq_item_port.item_done();
end
end
begin
wait_reset();
end
join
endtask
virtual protected task read(input logic [31:0] addr, ouput logic [31:0] data);
@(this.bif.master_ck);
this.bif.master_ck.psel <= '1;
this.bif.master_ck.penable <= '0;
this.bif.master_ck.pwrite <= '0;
this.bif.master_ck.paddr <= addr;
@(this.bif.master_ck);
this.bif.master_ck.penable <= '1;
@(this.bif.master_ck);
while(~this.bif.master_ck.pready)
@(this.bif.master_ck);
data <= this.bif.master_ck.prdata;
this.bif.master_ck.psel <= '0;
this.bif.master_ck.penable <= '0;
endtask
virtual protected task write(input logic [31:0] addr, output logic [31:0] data);
@(this.bif.master_ck);
this.bif.master_ck.paddr <= addr;
this.bif.master_ck.pwdata <= data;
this.bif.master_ck.pwrite <= '1;
this.bif.master_ck.psel <= '1;
this.bif.master_ck.penable <= '0;
@(this.bif.master_ck);
this.bif.master_ck.penable <= '1;
@(this.bif.master_ck);
while(~this.bif.master_ck.pready)
@(this.bif.master_ck);
this.bif.master_ck.psel <= '0;
this.bif.master_ck.penable <= '0;
this.bif.master_ck.pwdata <= '0;
endtask
virtual protected task wait_reset();
forever begin
@(posedge bif.presetn);
foreach(process[i])
processes[i].kill();
@(posedge bif.presetn);
end
endtask
endclass
七、apb agent
class apb_agent extends uvm_agent;
apb_driver drv;
apb_sequencer sqr;
`uvm_component_utils_begin(apb_agetn)
`uvm_field_object(drv,UVM_ALL_ON);
`uvm_field_object(sqr,UVM_ALL_ON);
`uvm_component_utils_end
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = apb_driver::type_id::create("drv",this);
sqr = apb_sequencer::type_id::create("sqr",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
八、apb agent pkg
package apb_agent_pkg;
import uvm_pkg::*:
`include "uvm_macros.svh"
import spi_reg_pkg::*;
`include "apb_transaction.svh"
`include "apb_sequencer.svh"
`include "apb_driver.svh"
`include "apb_adapter.svh"
`include "apb_agent.svh"
endpackage: apb_agent_pkg