电力电子转战数字IC20220819day64——uvm实战1A

目录

MCDF改造

APB接口

验证环境的改造

实战任务

apb_config

apb_master_agent

apb_master_driver

1.1实现apb_master_driver的drive_transfer()

apb_master_monitor

 1.3编写apb_master_monitor中的collect_transfer()

apb_master_sequence_Lib

1.2编写apb_master_sequence中的apb_master_write_seq::body()任务

apb_master_sequencer

slave端

apb_slave_driver

 1.4编写apb_slave_driver的drive_response()

apb_slave_seq_lib

为什么要让sequence控制driver?


这里涉及的是VIP的开发和MCDF用APB改造。

MCDF改造

 所有的接口都发生了改变,对应在硬件中都做了修改。

APB接口

 特点是一总(AHB或者AXI)多从,1总通过APB桥发放到后面的多从slave;2个clk完成1次W/R操作,用状态机表示为idle->setup->enable

验证环境的改造

由于接口几乎全部被更新,所以mcdf这一层的接口和tb的接口也需要做相应的更改。

这里似乎没有涉及到env的问题,等下看看具体代码。

实战任务

 

 直接看到APB部分的文件夹,可以看到文件数量大大增加,采取了`include的方式将文件分开了。

apb_pkg只有简单的几行代码,信息都包含在别的文件中。

`ifndef APB_PKG_SV
`define APB_PKG_SV

package apb_pkg;

import uvm_pkg::*;
`include "uvm_macros.svh"

`include "apb.svh"

endpackage : apb_pkg

   
`endif //  `ifndef APB_PKG_SV

apb_pkg include了apb.svh,而后者include了主从的各个组件的sv和svh文件。

`ifndef APB_SVH
`define APB_SVH


`include "apb_transfer.sv"
`include "apb_config.sv"

`include "apb_master_driver.svh"
`include "apb_master_monitor.svh"
`include "apb_master_sequencer.svh"
`include "apb_master_agent.svh"

`include "apb_slave_driver.svh"
`include "apb_slave_monitor.svh"
`include "apb_slave_sequencer.svh"
`include "apb_slave_agent.svh"


`include "apb_master_driver.sv"       
`include "apb_master_monitor.sv"
`include "apb_master_sequencer.sv"
`include "apb_master_agent.sv"
`include "apb_master_seq_lib.sv"

`include "apb_slave_driver.sv"       
`include "apb_slave_monitor.sv"
`include "apb_slave_sequencer.sv"
`include "apb_slave_agent.sv"
`include "apb_slave_seq_lib.sv"



   
`endif //  `ifndef APB_SVH

在apb_tb中,include了test.svh和if.sv,要跑起仿真只需要编译pkg和tb即可(其他文件都被include了,其实也会被编译到)。tb只做了接口的配置

`timescale 1ps/1ps
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "apb_tests.svh"
`include "apb_if.sv"
module apb_tb;
  bit clk, rstn;
  initial begin
    fork
      begin 
        forever #5ns clk = !clk;
      end
      begin
        #100ns;
        rstn <= 1'b1;
        #100ns;
        rstn <= 1'b0;
        #100ns;
        rstn <= 1'b1;
      end
    join_none
  end

  apb_if intf(clk, rstn);

  initial begin
    uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.mst", "vif", intf);
    uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.slv", "vif", intf);
    run_test("apb_single_transaction_test");
  end

endmodule

在apb_if中,包括了接口+时钟块,覆盖组covergroup。接口信号按照时序图给出,时钟块分为master和slave,信号的方向互补,monitor的时钟块所有信号都是input。

这里又遇到覆盖率收集的问题,复习一下。定义覆盖组后再initial块中例化各个覆盖组。

https://mp.csdn.net/mp_blog/creation/editor/125731520

`ifndef APB_IF_SV
`define APB_IF_SV

interface apb_if (input clk, input rstn);

  logic [31:0] paddr;
  logic        pwrite;
  logic        psel;
  logic        penable;
  logic [31:0] pwdata;
  logic [31:0] prdata;

  // Control flags
  bit                has_checks = 1;
  bit                has_coverage = 1;

  // Actual Signals 
  // USER: Add interface signals

  clocking cb_mst @(posedge clk);
    // USER: Add clocking block detail
    default input #1ps output #1ps;
    output paddr, pwrite, psel, penable, pwdata;
    input prdata;
  endclocking : cb_mst

  clocking cb_slv @(posedge clk);
   // USER: Add clocking block detail
    default input #1ps output #1ps;
    input paddr, pwrite, psel, penable, pwdata;
    output prdata;
  endclocking : cb_slv

  clocking cb_mon @(posedge clk);
   // USER: Add clocking block detail
    default input #1ps output #1ps;
    input paddr, pwrite, psel, penable, pwdata, prdata;
  endclocking : cb_mon

  // Coverage and assertions to be implemented here.
  // USER: Add assertions/coverage here

  // APB command covergroup
  covergroup cg_apb_command @(posedge clk iff rstn);
    pwrite: coverpoint pwrite{
      type_option.weight = 0;
      bins write = {1};
      bins read  = {0};

    }
    psel : coverpoint psel{
      type_option.weight = 0;
      bins sel   = {1};
      bins unsel = {0};
    }
    cmd  : cross pwrite, psel{
      bins cmd_write = binsof(psel.sel) && binsof(pwrite.write);
      bins cmd_read  = binsof(psel.sel) && binsof(pwrite.read);
      bins cmd_idle  = binsof(psel.unsel);
    }
  endgroup

  // APB transaction timing group
  covergroup cg_apb_trans_timing_group @(posedge clk iff rstn);
    psel: coverpoint psel{
      bins single   = (0 => 1 => 1  => 0); 
      bins burst_2  = (0 => 1 [*4]  => 0); 
      bins burst_4  = (0 => 1 [*8]  => 0); 
      bins burst_8  = (0 => 1 [*16] => 0); 
      bins burst_16 = (0 => 1 [*32] => 0); 
      bins burst_32 = (0 => 1 [*64] => 0); 
    }
    penable: coverpoint penable {
      bins single = (0 => 1 => 0 [*2:10] => 1);
      bins burst  = (0 => 1 => 0         => 1);
    }
  endgroup

  // APB write & read order group
  covergroup cg_apb_write_read_order_group @(posedge clk iff (rstn && penable));
    write_read_order: coverpoint pwrite{
      bins write_write = (1 => 1);
      bins write_read  = (1 => 0);
      bins read_write  = (0 => 1);
      bins read_read   = (0 => 0);
    } 
  endgroup

  initial begin
    automatic cg_apb_command cg0 = new();
    automatic cg_apb_trans_timing_group cg1 = new();
    automatic cg_apb_write_read_order_group cg2 = new();
  end

endinterface : apb_if

`endif // APB_IF_SV

关于test文件,画了个框图如下:

 接下来再逐个看各个组件

apb_config

这是等下用来配置agent是active还是passive用的object

class apb_config extends uvm_object;

  `uvm_object_utils(apb_config)

  // USER to specify the config items
  uvm_active_passive_enum  is_active = UVM_ACTIVE;
  
  function new (string name = "apb_config");
    super.new(name);
  endfunction : new


endclass

`endif // APB_CONFIG_SV

apb_master_agent

由于使用了头文件,这里的方法名都要加上apb_master_agent::

agent做了三件事:build()例化对象,connect()连接driver和sequencer(TLM通信,两个组件自带的seq_item_PORT)

assign_vi配置接口

//头文件

`ifndef APB_MASTER_AGENT_SVH
`define APB_MASTER_AGENT_SVH

class apb_master_agent extends uvm_agent;
  apb_config cfg;
  apb_master_driver driver;
  apb_master_sequencer sequencer;
  apb_master_monitor monitor;
  virtual apb_if vif;

  `uvm_component_utils_begin(apb_master_agent)
    // USER: Register your fields here
  `uvm_component_utils_end

  // new - constructor
  extern function new (string name, uvm_component parent);

  // uvm build phase
  extern function void build();

  // uvm connection phase
  extern function void connect();
  
  // This method assigns the virtual interfaces to the agent's children
  extern function void assign_vi(virtual apb_if vif);
endclass : apb_master_agent

`endif // APB_MASTER_AGENT_SVH

//sv文件

`ifndef APB_MASTER_AGENT_SV
`define APB_MASTER_AGENT_SV
//由于使用了头文件,这里的方法名都要加上apb_master_agent::
function apb_master_agent::new(string name, uvm_component parent);
  super.new(name, parent);
endfunction : new


function void apb_master_agent::build();
  super.build();
  // get config
  if( !uvm_config_db#(apb_config)::get(this,"","cfg", cfg)) begin
    `uvm_warning("GETCFG","cannot get config object from config DB")
     cfg = apb_config::type_id::create("cfg");
  end
  // get virtual interface
  if( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin
    `uvm_fatal("GETVIF","cannot get vif handle from config DB")
  end
  monitor = apb_master_monitor::type_id::create("monitor",this);
  monitor.cfg = cfg;
  if(cfg.is_active == UVM_ACTIVE) begin
    sequencer = apb_master_sequencer::type_id::create("sequencer",this);
    sequencer.cfg = cfg;
    driver = apb_master_driver::type_id::create("driver",this);
    driver.cfg = cfg;
  end
endfunction : build

function void apb_master_agent::connect();
  assign_vi(vif);

  if(is_active == UVM_ACTIVE) begin
    driver.seq_item_port.connect(sequencer.seq_item_export);       
  end

endfunction : connect
  
function void apb_master_agent::assign_vi(virtual apb_if vif);
   monitor.vif = vif;
   if (is_active == UVM_ACTIVE) begin
      sequencer.vif = vif; 
      driver.vif = vif; 
    end
endfunction : assign_vi


`endif // APB_MASTER_AGENT_SV

apb_master_driver


`ifndef APB_MASTER_DRIVER_SVH
`define APB_MASTER_DRIVER_SVH

class apb_master_driver extends uvm_driver #(apb_transfer);
  apb_config cfg;

  `uvm_component_utils_begin(apb_master_driver)

  `uvm_component_utils_end

  extern function new (string name, uvm_component parent);

  extern virtual task run();

  virtual apb_if vif;

  extern virtual protected task get_and_drive();
 
  extern virtual protected task drive_transfer(apb_transfer t);

  extern virtual protected task reset_listener();
 
  extern protected task do_idle();

  extern protected task do_write(apb_transfer t);

  extern protected task do_read(apb_transfer t);

endclass : apb_master_driver

`endif // APB_MASTER_DRIVER_SVH


 driver的run_phase同时运行两个线程:get and drive和复位。

而1.1就是要编写drive这个方法,对应APB的idle和读写,分别对应三个方法。在三个方法中根据时序图编写,addr和write在idle时保持不变(为了省电),时序图和代码如下。

 

 


`ifndef APB_MASTER_DRIVER_SV
`define APB_MASTER_DRIVER_SV

function apb_master_driver::new (string name, uvm_component parent);
  super.new(name, parent);
endfunction : new

task apb_master_driver::run();
   fork
     get_and_drive();
     reset_listener();
   join_none
endtask : run

task apb_master_driver::get_and_drive();
  forever begin
    seq_item_port.get_next_item(req);
    `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
    drive_transfer(req);
    void'($cast(rsp, req.clone()));
    rsp.set_sequence_id(req.get_sequence_id());
    seq_item_port.item_done(rsp);
    `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
  end
endtask : get_and_drive

task apb_master_driver::drive_transfer (apb_transfer t);
  `uvm_info(get_type_name(), "drive_transfer", UVM_HIGH)
  case(t.trans_kind)
    IDLE    : this.do_idle();
    WRITE   : this.do_write(t);
    READ    : this.do_read(t);
    default : `uvm_error("ERRTYPE", "unrecognized transaction type")
  endcase
endtask : drive_transfer

task apb_master_driver::do_write(apb_transfer t);
  `uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
  @(vif.cb_mst);
  vif.cb_mst.paddr <= t.addr;
  vif.cb_mst.pwrite <= 1;
  vif.cb_mst.psel <= 1;
  vif.cb_mst.penable <= 0;
  vif.cb_mst.pwdata <= t.data;
  @(vif.cb_mst);
  vif.cb_mst.penable <= 1;
  repeat(t.idle_cycles) this.do_idle();
endtask: do_write

task apb_master_driver::do_read(apb_transfer t);
  `uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
  @(vif.cb_mst);
  vif.cb_mst.paddr <= t.addr;
  vif.cb_mst.pwrite <= 0;
  vif.cb_mst.psel <= 1;
  vif.cb_mst.penable <= 0;
  @(vif.cb_mst);
  vif.cb_mst.penable <= 1;
  #100ps;//为了采样,人为添加一个延迟,避免detal cycle
  t.data = vif.prdata;
  repeat(t.idle_cycles) this.do_idle();
endtask: do_read

task apb_master_driver::do_idle();
  `uvm_info(get_type_name(), "do_idle ...", UVM_HIGH)
  @(vif.cb_mst);
  //vif.cb_mst.paddr <= 0;
  //vif.cb_mst.pwrite <= 0;
  vif.cb_mst.psel <= 0;
  vif.cb_mst.penable <= 0;
  vif.cb_mst.pwdata <= 0;
endtask:do_idle

task apb_master_driver::reset_listener();
  `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
  fork
    forever begin
      @(negedge vif.rstn); // ASYNC reset
      vif.paddr <= 0;
      vif.pwrite <= 0;
      vif.psel <= 0;
      vif.penable <= 0;
      vif.pwdata <= 0;
    end
  join_none
endtask

`endif // APB_MASTER_DRIVER_SV

1.1实现apb_master_driver的drive_transfer()

task apb_master_driver::drive_transfer (apb_transfer t);
  `uvm_info(get_type_name(), "drive_transfer", UVM_HIGH)
  case(t.trans_kind)
    IDLE    : this.do_idle();
    WRITE   : this.do_write(t);
    READ    : this.do_read(t);
    default : `uvm_error("ERRTYPE", "unrecognized transaction type")
  endcase
endtask : drive_transfer

//其中apb_transfer如下,就是个item

`ifndef APB_TRANSFER_SV
`define APB_TRANSFER_SV
typedef enum {IDLE, WRITE, READ } apb_trans_kind;


class apb_transfer extends uvm_sequence_item;
  rand bit [31:0]      addr;
  rand bit [31:0]      data;
  rand apb_trans_kind  trans_kind; 
  rand int idle_cycles;

  constraint cstr{
    soft idle_cycles == 1;  };

  `uvm_object_utils_begin(apb_transfer)
    `uvm_field_enum     (apb_trans_kind, trans_kind, UVM_ALL_ON)
    `uvm_field_int      (addr, UVM_ALL_ON)
    `uvm_field_int      (data, UVM_ALL_ON)
    `uvm_field_int      (idle_cycles, UVM_ALL_ON)
  `uvm_object_utils_end

  function new (string name = "apb_transfer_inst");
    super.new(name);
  endfunction : new

endclass : apb_transfer

`endif // APB_TRANSFER_SV

再来看monitor

apb_master_monitor

TLM通信端口是uvm_analysis_port 


`ifndef APB_MASTER_MONITOR_SVH
`define APB_MASTER_MONITOR_SVH

class apb_master_monitor extends uvm_monitor;
  apb_config cfg;
  bit checks_enable = 1;
  bit coverage_enable = 1;
  virtual apb_if vif;

  uvm_analysis_port #(apb_transfer) item_collected_port;

  `uvm_component_utils_begin(apb_master_monitor)
     `uvm_field_int(checks_enable, UVM_ALL_ON)
     `uvm_field_int(coverage_enable, UVM_ALL_ON)

  `uvm_component_utils_end
   
   extern function new(string name, uvm_component parent=null);

   extern function void build();

   extern virtual task run();

  event apb_master_cov_transaction;

  covergroup apb_master_cov_trans @apb_master_cov_transaction;

  endgroup : apb_master_cov_trans

  protected apb_transfer trans_collected;

  extern virtual protected task monitor_transactions();

  extern virtual protected task collect_transfer();

  extern protected function void perform_transfer_checks();

  extern protected function void perform_transfer_coverage();

endclass : apb_master_monitor

`endif // APB_MASTER_MONITOR_SVH

`ifndef APB_MASTER_MONITOR_SV
`define APB_MASTER_MONITOR_SV

function apb_master_monitor::new(string name, uvm_component parent=null);
  super.new(name, parent);
  item_collected_port = new("item_collected_port",this);
  trans_collected = new();
endfunction:new

// build
function void apb_master_monitor::build();
   super.build();
endfunction : build  

task apb_master_monitor::monitor_transactions();
   forever begin
 
      // Extract data from interface into transaction
      collect_transfer();

      // Check transaction
      if (checks_enable)
 	      perform_transfer_checks();

      // Update coverage
      if (coverage_enable)
 	      perform_transfer_coverage();

      // Publish to subscribers
      item_collected_port.write(trans_collected);

   end
endtask // monitor_transactions
   

task apb_master_monitor::run();
  fork
    monitor_transactions();
  join_none
endtask // run
  
  
task apb_master_monitor::collect_transfer();
  apb_transfer t;
  // Advance clock
  @(vif.cb_mon);
  if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
    t = apb_transfer::type_id::create("t");
    case(vif.cb_slv.pwrite)
      1'b1    : begin
                  @(vif.cb_mon);//在第二个周期
                  t.addr = vif.cb_mon.paddr;
                  t.data = vif.cb_mon.pwdata;
                  t.trans_kind = WRITE;
                end 
      1'b0    : begin
                  @(vif.cb_mon);
                  t.addr = vif.cb_mon.paddr;
                  t.data = vif.cb_mon.prdata;
                  t.trans_kind = READ;
                end
      default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
    endcase
    item_collected_port.write(t);
  end
endtask: collect_transfer 


// perform_transfer_checks
function void apb_master_monitor::perform_transfer_checks();

 // USER: do some checks on the transfer here

endfunction : perform_transfer_checks

// perform_transfer_coverage
function void apb_master_monitor::perform_transfer_coverage();

 // USER: coverage implementation
  -> apb_master_cov_transaction;	

endfunction : perform_transfer_coverage

`endif // APB_MASTER_MONITOR_SV

 1.3编写apb_master_monitor中的collect_transfer()

用于从接口获取item过来。

在setup阶段判断是读还是写,注意!要多用一次@(vif.cb_mon),APB在第二个周期完成读写操作

然后把item通过analysis port的write函数写入item

task apb_master_monitor::collect_transfer();
  apb_transfer t;
  // Advance clock
  @(vif.cb_mon);
  if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
    t = apb_transfer::type_id::create("t");
    case(vif.cb_slv.pwrite)
      1'b1    : begin
                  @(vif.cb_mon);//在第二个周期
                  t.addr = vif.cb_mon.paddr;
                  t.data = vif.cb_mon.pwdata;
                  t.trans_kind = WRITE;
                end 
      1'b0    : begin
                  @(vif.cb_mon);
                  t.addr = vif.cb_mon.paddr;
                  t.data = vif.cb_mon.prdata;
                  t.trans_kind = READ;
                end
      default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
    endcase
    item_collected_port.write(t);
  end
endtask: collect_transfer 

apb_master_sequence_Lib

包含base seq和各个seq,包括写1个,读1个,写完读1个,连续写,连续读,每次读写都需要一次`uvm_do_with,读完要把总线设为idle

1.2编写apb_master_sequence中的apb_master_write_seq::body()任务

base sequence只注册和例化,等待具体的子类来扩展。

一共有5个seq,如上所示。

single_write的body,发送一个req过去后获取一个rsp

  virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
	  `uvm_do_with(req, {trans_kind == WRITE; addr == local::addr; data == local::data;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

single_read的body,发送req过去后获取rsp并将rsp的data给到data,完成读出

  virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
	  `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
    get_response(rsp);
    data = rsp.data;
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

 write_read的body,写的时候要限定类型和idle_cycle,拿到rsp后读,读只限定类型和地址即可

  virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
	  `uvm_do_with(req,  {trans_kind == WRITE; 
                        addr == local::addr; 
                        data == local::data;
                        idle_cycles == local::idle_cycles;
                       })
    get_response(rsp);
    `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
    get_response(rsp);
    data = rsp.data;
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

 burst_write的body,写完拿到rsp后要将类型置为IDLE

不明白为什么地址要addr == local::addr + (i<<2);

  virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    foreach(data[i]) begin
	    `uvm_do_with(req, {trans_kind == WRITE; 
                         addr == local::addr + (i<<2); 
                         data == local::data[i];
                         idle_cycles == 0;
                        })
      get_response(rsp);
    end
    `uvm_do_with(req, {trans_kind == IDLE;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

 burst_read的body同理。

  virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    foreach(data[i]) begin
	    `uvm_do_with(req, {trans_kind == READ; 
                         addr == local::addr + (i<<2); 
                         idle_cycles == 0;
                        })
      get_response(rsp);
      data[i] = rsp.data;
    end
    `uvm_do_with(req, {trans_kind == IDLE;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

apb_master_sequencer

只有注册和new函数例化

slave端

agent是一样的

apb_slave_driver

由于要接收master端的item,这里有个关联数组mem。

之前的mem出现在test的base_test_sequence中,用来存放读写数据。

与master相对应的是drive req,这里是drive_response,这也作为一个并行线程(master是两个并行线程)

复位时要清空mem的数据,因为这边是从端


`ifndef APB_SLAVE_DRIVER_SV
`define APB_SLAVE_DRIVER_SV

function apb_slave_driver::new (string name, uvm_component parent);
  super.new(name, parent);
endfunction : new

task apb_slave_driver::run();
   fork
     get_and_drive();
     reset_listener();
     drive_response();
   join_none
endtask : run

task apb_slave_driver::get_and_drive();
  forever begin
    seq_item_port.get_next_item(req);
    `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
    void'($cast(rsp, req.clone()));
    rsp.set_sequence_id(req.get_sequence_id());
    seq_item_port.item_done(rsp);
    `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
  end
endtask : get_and_drive

task apb_slave_driver::drive_response();
  `uvm_info(get_type_name(), "drive_response", UVM_HIGH)
  forever begin
    @(vif.cb_slv);
    if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
      case(vif.cb_slv.pwrite)
        1'b1    : this.do_write();
        1'b0    : this.do_read();
        default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
      endcase
    end
    else begin
      this.do_idle();
    end
  end
endtask : drive_response

task apb_slave_driver::reset_listener();
  `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
  fork
    forever begin
      @(negedge vif.rstn); // ASYNC reset
      vif.prdata <= 0;
      this.mem.delete(); // reset internal memory
    end
  join_none
endtask: reset_listener

task apb_slave_driver::do_idle();
  `uvm_info(get_type_name(), "do_idle", UVM_HIGH)
  vif.cb_slv.prdata <= 0;
endtask: do_idle

task apb_slave_driver::do_write();
  bit[31:0] addr;
  bit[31:0] data;
  `uvm_info(get_type_name(), "do_write", UVM_HIGH)
  @(vif.cb_slv);
  addr = vif.cb_slv.paddr;
  data = vif.cb_slv.pwdata;
  mem[addr] = data;
endtask: do_write

task apb_slave_driver::do_read();
  bit[31:0] addr;
  bit[31:0] data;
  `uvm_info(get_type_name(), "do_read", UVM_HIGH)
  wait(vif.penable === 1'b1);
  addr = vif.cb_slv.paddr;
  if(mem.exists(addr))
    data = mem[addr];
  else
    data = 0;
  #1ps;
  vif.prdata <= data;
  @(vif.cb_slv);
endtask: do_read

`endif // APB_SLAVE_DRIVER_SV

 1.4编写apb_slave_driver的drive_response()

还是APB的状态机在setup判断是读还是写,对应读写函数。

task apb_slave_driver::drive_response();
  `uvm_info(get_type_name(), "drive_response", UVM_HIGH)
  forever begin
    @(vif.cb_slv);
    if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
      case(vif.cb_slv.pwrite)
        1'b1    : this.do_write();
        1'b0    : this.do_read();
        default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
      endcase
    end
    else begin
      this.do_idle();
    end
  end
endtask : drive_response

 读写函数另外编写,按照时序图给出对应的信号。

task apb_slave_driver::do_idle();
  `uvm_info(get_type_name(), "do_idle", UVM_HIGH)
  vif.cb_slv.prdata <= 0;
endtask: do_idle

task apb_slave_driver::do_write();
  bit[31:0] addr;
  bit[31:0] data;
  `uvm_info(get_type_name(), "do_write", UVM_HIGH)
  @(vif.cb_slv);
  addr = vif.cb_slv.paddr;
  data = vif.cb_slv.pwdata;
  mem[addr] = data;
endtask: do_write

task apb_slave_driver::do_read();
  bit[31:0] addr;
  bit[31:0] data;
  `uvm_info(get_type_name(), "do_read", UVM_HIGH)
  wait(vif.penable === 1'b1);
  addr = vif.cb_slv.paddr;
  if(mem.exists(addr))
    data = mem[addr];
  else
    data = 0;
  #1ps;
  vif.prdata <= data;
  @(vif.cb_slv);
endtask: do_read

apb_slave_seq_lib

只是发送了一个item给driver,没法控制driver。

为什么要让sequence控制driver?

`ifndef APB_SLAVE_SEQ_LIB_SV
`define APB_SLAVE_SEQ_LIB_SV

class example_apb_slave_seq extends uvm_sequence #(apb_transfer);

    function new(string name=""); 
      super.new(name);
    endfunction : new
  
  `uvm_object_utils(example_apb_slave_seq)    

    apb_transfer this_transfer;
  
    virtual task body();
      `uvm_info(get_type_name(),"Starting example sequence", UVM_HIGH)
       `uvm_do(this_transfer) 
	
      `uvm_info(get_type_name(),$psprintf("Done example sequence: %s",this_transfer.convert2string()), UVM_HIGH)
 
    endtask
  
endclass : example_apb_slave_seq

`endif // apb_SLAVE_SEQ_LIB_SV

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值