Search code examples
verilogsystem-veriloguvmtest-benchedaplayground

Why does APB testbench not send data into the prdata register?


Here is my code on EDA Playground.

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

//////////////////////transaction class//////////////////////
class transaction extends uvm_sequence_item;
  `uvm_object_utils( transaction )
  rand    bit     [7 : 0]    paddr;
  rand    bit     [7 : 0]    pwdata;
  bit             [7 : 0]    prdata;
  rand    bit                pwrite;
  
    function new(string name = "transaction");
       super.new(name);
    endfunction 
  
    constraint c_paddr { 
      paddr inside {8'hF0, 8'hE0, 8'hE1, 8'hE2, 8'hE3, 8'hD0, 8'hD1, 8'hD2, 8'hD3};
    }

endclass

//////////////////////Driver//////////////////////
class driver extends uvm_driver #(transaction);
    `uvm_component_utils(driver)
  
  virtual top_if vif;
  transaction tr;
  
  function new(string name = "driver", uvm_component parent = null);
        super.new(name, parent);
   endfunction  
  
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
      if(!uvm_config_db#(virtual top_if)::get(this, "", "vif", vif))
        `uvm_error("DRV", "Error getting Interface Handle")
    endfunction 
        
    /////write data to dut  -> psel -> pen 
        
    virtual task write();
        @(posedge vif.pclk);
        vif.paddr   <= tr.paddr;
        vif.pwdata  <= tr.pwdata;
        vif.pwrite  <= 1'b1;
        vif.psel    <= 1'b1;

        @(posedge vif.pclk);
        vif.penable <= 1'b1;

      `uvm_info("DRV", $sformatf("Mode :Write | WDATA : %0d ADDR : %0d", vif.pwdata, vif.paddr), UVM_NONE);      
   
         @(posedge vif.pclk);
        vif.psel    <= 1'b0;
        vif.penable <= 1'b0;
    endtask 
      
     ////read data from dut 
    virtual task read();
        @(posedge vif.pclk);
        vif.paddr   <= tr.paddr;
        vif.pwrite  <= 1'b0;
        vif.psel    <= 1'b1;

        @(posedge vif.pclk);
        vif.penable <= 1'b1;

      `uvm_info("DRV", $sformatf("Mode :Read | WDATA : %0d ADDR : %0d RDATA : %0d", vif.pwdata, vif.paddr, vif.prdata), UVM_NONE);

        @(posedge vif.pclk);
        vif.psel    <= 1'b0;
        vif.penable <= 1'b0;
        tr.prdata   = vif.prdata;
      

    endtask 
      
    /////////////////////////////////////////
      
    virtual task run_phase (uvm_phase phase);
      bit [7:0] data;
        vif.presetn <= 1'b1;
        vif.psel <= 0;
        vif.penable <= 0;
        vif.pwrite <= 0;
        vif.paddr <= 0;
        vif.pwdata <= 0;
        forever begin
          seq_item_port.get_next_item (tr);
          if (tr.pwrite)
            begin
               write();
            end
            else 
            begin   
               read();
            end
            seq_item_port.item_done ();
        end
    endtask  
endclass
      
      
      
//////////////////////Monitor//////////////////////
      class monitor extends uvm_monitor;
    `uvm_component_utils( monitor )

    uvm_analysis_port   #(transaction)  mon_ap;
    virtual     top_if              vif;
    
    function new(string name="my_monitor", uvm_component parent);
        super.new(name, parent);
    endfunction : new
  
    virtual function void build_phase(uvm_phase phase);
        super.build_phase (phase);
        mon_ap = new("mon_ap", this);
      if(! uvm_config_db#(virtual top_if)::get (this, "", "vif", vif))
            `uvm_error("DRV", "Error getting Interface Handle")
    endfunction : build_phase
  
    virtual task run_phase(uvm_phase phase);
        fork
            forever begin
               @(posedge vif.pclk);
                if(vif.psel && vif.penable && vif.presetn) begin
                  transaction tr = transaction::type_id::create("tr");
                  tr.paddr  = vif.paddr;
                  tr.pwrite = vif.pwrite;
                    if (vif.pwrite)
                       begin
                        tr.pwdata = vif.pwdata;
                        @(posedge vif.pclk);
                         `uvm_info("MON", $sformatf("Mode : Write | WDATA : %0d ADDR : %0d", vif.pwdata, vif.paddr), UVM_NONE);
                       end
                    else
                       begin
                         @(posedge vif.pclk);
                        tr.prdata = vif.prdata;
                         `uvm_info("MON", $sformatf("Mode : Read | WDATA : %0d ADDR : %0d RDATA : %0d", vif.pwdata, vif.paddr, vif.prdata), UVM_NONE); 
                       end
                  mon_ap.write(tr);
                end 
            end
        join_none
    endtask 

endclass
      
      
      
//////////////////////scoreboard//////////////////////
class sco extends uvm_scoreboard;
`uvm_component_utils(sco)

  uvm_analysis_imp#(transaction,sco) recv;
  bit [7:0] arr [1024];
  bit [7:0] temp;
 

    function new(input string inst = "sco", uvm_component parent = null);
    super.new(inst,parent);
    endfunction
    
    virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    recv = new("recv", this);
    endfunction
        
  virtual function void write(transaction tr);
    
    if(tr.pwrite == 1'b1)
      begin
        arr[tr.paddr] = tr.pwdata;

        `uvm_info("SCO", $sformatf("DATA Stored | Addr : %0d Data :%0d", tr.paddr, tr.pwdata), UVM_NONE)
      end
    else
       begin
         
         temp = arr[tr.paddr];                   

         if( temp == tr.prdata)
           `uvm_info("SCO", $sformatf("Test Passed -> Addr : %0d Data :%0d", tr.paddr, temp), UVM_NONE)
         else
          `uvm_error("SCO", $sformatf("Test Failed -> Addr : %0d Data :%0d", tr.paddr, temp))
         
       end
       $display("----------------------------------------------------------------");           
      endfunction
endclass

         
         
//////////////////////Agent//////////////////////
class agent extends uvm_agent;
`uvm_component_utils(agent)

function new(input string inst = "agent", uvm_component parent = null);
super.new(inst,parent);
endfunction

 driver d;
 uvm_sequencer#(transaction) seqr;
 monitor m;

virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
   d = driver::type_id::create("d",this);
   m = monitor::type_id::create("m",this);
   seqr = uvm_sequencer#(transaction)::type_id::create("seqr", this); 
endfunction

virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
   d.seq_item_port.connect(seqr.seq_item_export);
endfunction

endclass
         
         
//////////////////////RAL Model//////////////////////
/////////// Status Register//////////////

///////// s_reg Registers//////////////
class status_reg extends uvm_reg;
  `uvm_object_utils(status_reg)
  rand uvm_reg_field status;
  rand uvm_reg_field producer;
  rand uvm_reg_field consumer;

  function new(string name = "status_reg");
    super.new(name, 8, UVM_NO_COVERAGE);
  endfunction

  function void build();
    status   = uvm_reg_field::type_id::create("status");
    producer = uvm_reg_field::type_id::create("producer");
    consumer = uvm_reg_field::type_id::create("consumer");
    //(reg, bitwidth, lsb, access, volatile, reselVal, hasReset, isRand, fieldAccess)
    status.configure(this, 1, 0, "RW", 0, 0, 1, 1, 1);
    producer.configure(this, 1, 1, "RW", 0, 0, 1, 1, 1);
    consumer.configure(this, 1, 2, "RW", 0, 0, 1, 1, 1);
  endfunction
endclass

////////////Status register block////////////////
class status_reg_block extends uvm_reg_block;
  `uvm_object_utils(status_reg_block)
   status_reg s_reg0;
   status_reg s_reg1;
  
  uvm_reg_map status_reg_map;
  
  function new(string name = "status_reg_block");
    super.new(name, UVM_NO_COVERAGE);
  endfunction

  virtual function void build();
    s_reg0 = status_reg::type_id::create("s_reg0");
    s_reg0.configure(this);
    s_reg0.build();
    
    s_reg1 = status_reg::type_id::create("s_reg1");
    s_reg1.configure(this);
    s_reg1.build();
    
    status_reg_map = create_map("status_reg_map", 8'hF0, 1, UVM_LITTLE_ENDIAN);
    status_reg_map.add_reg(s_reg0, 'h00, "RW");
    status_reg_map.add_reg(s_reg1, 'h01, "RW");
  endfunction
endclass

//////////////////Duplicate Register//////////////

///////////// control register///////////////////////
class ctrl_d_reg extends uvm_reg;
  `uvm_object_utils(ctrl_d_reg)
  
  rand uvm_reg_field d_enb_field;
  rand uvm_reg_field d_enable_field;
  rand uvm_reg_field d_r_w_field;
  
  function new(string name = "ctrl_d_reg");
    super.new(name, 8, UVM_NO_COVERAGE);
  endfunction

  function void build();
    d_enb_field = uvm_reg_field::type_id::create("d_enb_field");
    d_enable_field = uvm_reg_field::type_id::create("d_enable_field");
    d_r_w_field = uvm_reg_field::type_id::create("d_r_w_field");
    
    d_enb_field.configure(  this, 1, 0, "RW", 0, 0, 1, 1, 1);
    d_enable_field.configure(   this, 1, 1, "RW", 0, 0, 1, 1, 1);
    d_r_w_field.configure(  this, 1, 2, "RW", 0, 0, 1, 1, 1);
  endfunction
  
endclass


///////////// d_reg registers///////////////////////
class data_reg extends uvm_reg;
  `uvm_object_utils(data_reg)
  
  rand uvm_reg_field data_reg_field;
  
  function new(string name = "data_reg");
    super.new(name, 8, UVM_NO_COVERAGE);
  endfunction

  function void build();
    data_reg_field = uvm_reg_field::type_id::create("data_reg_field");
    data_reg_field.configure(this, 8, 0, "RW", 0, 0, 1, 1, 1);
  endfunction
  
endclass


//////////Data register 0 block/////////////
class data_reg_0_block extends uvm_reg_block;
  `uvm_object_utils(data_reg_0_block)
  
   ctrl_d_reg   enable_0;
   data_reg     baud_rate_0;
   data_reg     trans_count_0;
   data_reg     sl_address_0;
  
  uvm_reg_map data_reg_0_map;
  
  function new(string name = "data_reg_0_block");
    super.new(name, UVM_NO_COVERAGE);
  endfunction

  virtual function void build();
    enable_0      = ctrl_d_reg::type_id::create("enable_0");
    baud_rate_0   = data_reg::type_id::create("baud_rate_0");
    trans_count_0 = data_reg::type_id::create("trans_count_0");
    sl_address_0  = data_reg::type_id::create("sl_address_0");
    
    enable_0.build();
    baud_rate_0.build();
    trans_count_0.build();
    sl_address_0.build();
    
    enable_0.configure(this);
    baud_rate_0.configure(this);
    trans_count_0.configure(this);
    sl_address_0.configure(this);
   
    //check the address 00,01,02,03 and what is this below 4
    data_reg_0_map = create_map("data_reg_0_map", 8'hE0, 1, UVM_LITTLE_ENDIAN);
    data_reg_0_map.add_reg(enable_0,        'h00, "RW");
    data_reg_0_map.add_reg(baud_rate_0,     'h01, "RW");
    data_reg_0_map.add_reg(trans_count_0,   'h02, "RW");
    data_reg_0_map.add_reg(sl_address_0,    'h03, "RW");
  endfunction
endclass

//////////Data register 1 block/////////////
class data_reg_1_block extends uvm_reg_block;
  `uvm_object_utils(data_reg_1_block)
  
   ctrl_d_reg   enable;
   data_reg     baud_rate;
   data_reg     trans_count;
   data_reg     sl_address;
  
  uvm_reg_map data_reg_1_map;
  
  function new(string name = "data_reg_1_block");
    super.new(name, UVM_NO_COVERAGE);
  endfunction

  virtual function void build();
    enable      = ctrl_d_reg::type_id::create("enable");
    baud_rate   = data_reg::type_id::create("baud_rate");
    trans_count = data_reg::type_id::create("trans_count");
    sl_address  = data_reg::type_id::create("sl_address");
   
    enable.build();
    baud_rate.build();
    trans_count.build();
    sl_address.build();
    
    enable.configure(this);
    baud_rate.configure(this);
    trans_count.configure(this);
    sl_address.configure(this);
   
    data_reg_1_map = create_map("data_reg_1_map", 8'hD0, 1, UVM_LITTLE_ENDIAN);
    data_reg_1_map.add_reg(enable,      'h00, "RW");
    data_reg_1_map.add_reg(baud_rate,   'h01, "RW");
    data_reg_1_map.add_reg(trans_count, 'h02, "RW");
    data_reg_1_map.add_reg(sl_address,  'h03, "RW");
  endfunction
endclass


/////////////Ping pong register block///////////////////
class ping_pong_reg_block extends uvm_reg_block;
  `uvm_object_utils(ping_pong_reg_block)
  
   status_reg_block status_regs;
   data_reg_0_block d_reg_0;
   data_reg_1_block d_reg_1;
  
  uvm_reg_map ping_pong_reg_map;
  
  function new(string name = "ping_pong_reg_block");
    super.new(name, UVM_NO_COVERAGE);
  endfunction

  function void build();
    status_regs = status_reg_block::type_id::create("status_regs");
    d_reg_0 = data_reg_0_block::type_id::create("d_reg_0");
    d_reg_1 = data_reg_1_block::type_id::create("d_reg_1");
    
    status_regs.configure(this);
    d_reg_0.configure(this);
    d_reg_1.configure(this);
    
    status_regs.build();
    d_reg_0.build();
    d_reg_1.build();
    
    ping_pong_reg_map = create_map("ping_pong_reg_map", 8'hD0, 1, UVM_LITTLE_ENDIAN);
    ping_pong_reg_map.add_submap(d_reg_1.data_reg_1_map, 'h00);
    ping_pong_reg_map.add_submap(d_reg_0.data_reg_0_map, 'h10);
    ping_pong_reg_map.add_submap(status_regs.status_reg_map,'h20);
    
    lock_model();
  endfunction
endclass

         
         
         
//////////////////////Adapter//////////////////////
class top_adapter extends uvm_reg_adapter;
  `uvm_object_utils (top_adapter)

  function new (string name = "top_adapter");
      super.new (name);
   endfunction
  
  function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    transaction tr;    
    tr = transaction::type_id::create("tr");
    
    tr.pwrite    = (rw.kind == UVM_WRITE) ? 1'b1 : 1'b0;
    tr.paddr     = rw.addr;

    if(tr.pwrite) tr.pwdata =rw.data;
    if(!tr.pwrite) tr.prdata=rw.data;;
    //tr.pwdata    = rw.data;

    return tr;
  endfunction


   
  function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    transaction tr;
    
    assert($cast(tr, bus_item));

    rw.kind = (tr.pwrite == 1'b1) ? UVM_WRITE : UVM_READ;
    rw.data = (tr.pwrite == 1'b1) ? tr.pwdata : tr.prdata;
    rw.addr = tr.paddr;
    //rw.data = tr.prdata;

    rw.status = UVM_IS_OK;
  endfunction
endclass


//////////////////////Environment//////////////////////
class env extends uvm_env;
  `uvm_component_utils(env)
  
  agent         agent_inst;
  ping_pong_reg_block   regmodel;   
  top_adapter   adapter_inst;
  
  uvm_reg_predictor #(transaction)  predictor_inst;
   
  sco s; 
  
  function new(string name = "env", uvm_component parent);
    super.new(name, parent);
  endfunction : new

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    agent_inst = agent::type_id::create("agent_inst", this);
    s = sco::type_id::create("s", this);
    
    regmodel = ping_pong_reg_block::type_id::create("regmodel", this);
    regmodel.build();

   
    predictor_inst = uvm_reg_predictor#(transaction)::type_id::create("predictor_inst", this);
    adapter_inst = top_adapter::type_id::create("adapter_inst",, get_full_name());
    
  endfunction 

  function void connect_phase(uvm_phase phase);
    agent_inst.m.mon_ap.connect(s.recv);
    agent_inst.m.mon_ap.connect(predictor_inst.bus_in);
    
    regmodel.ping_pong_reg_map.set_sequencer( .sequencer(agent_inst.seqr), .adapter(adapter_inst) );
    regmodel.ping_pong_reg_map.set_base_addr(8'hD0);
    
    predictor_inst.map       = regmodel.ping_pong_reg_map;
    predictor_inst.adapter   = adapter_inst;
  endfunction 

endclass 

//////////////////////Sequences//////////////////////
class data_reg_wr extends uvm_sequence;
  `uvm_object_utils(data_reg_wr)
  ping_pong_reg_block regmodel;
  
  function new (string name = "data_reg_wr"); 
    super.new(name);    
  endfunction
  
  task body;  
    uvm_status_e   status;
    bit [7:0] wdata; 
    
    wdata = $urandom(5);
      regmodel.d_reg_0.baud_rate_0.write(status, wdata);
      regmodel.d_reg_1.baud_rate.write(status, wdata);

  endtask
endclass

class data_reg_rd extends uvm_sequence;
  `uvm_object_utils(data_reg_rd)
  ping_pong_reg_block regmodel;
  
  function new (string name = "data_reg_rd"); 
    super.new(name);    
  endfunction
  
  task body;  
    uvm_status_e   status;
    bit [7:0] rdata;
    
      regmodel.d_reg_0.baud_rate_0.read(status, rdata);
    //`uvm_info("SEQ", $sformatf("rData %0d",rdata), UVM_NONE)
      regmodel.d_reg_1.baud_rate.read(status, rdata);

  endtask
endclass


//////////////////////Test//////////////////////
class test extends uvm_test;
  `uvm_component_utils(test)

  function new(input string inst = "test", uvm_component c);
    super.new(inst, c);
  endfunction

  env e;
  data_reg_wr wr_seq;
  data_reg_rd rd_seq;

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    e = env::type_id::create("env", this);
    
    wr_seq = data_reg_wr::type_id::create("wr_seq");
    rd_seq = data_reg_rd::type_id::create("rd_seq");
  endfunction

  virtual task run_phase(uvm_phase phase);
    phase.raise_objection(this);

    `uvm_info("TEST", "Starting Write Sequence", UVM_MEDIUM)
    wr_seq.regmodel = e.regmodel;
    wr_seq.start(e.agent_inst.seqr);

    `uvm_info("TEST", "Starting Read Sequence", UVM_MEDIUM)
    rd_seq.regmodel = e.regmodel;
    rd_seq.start(e.agent_inst.seqr);

    phase.drop_objection(this);
    phase.phase_done.set_drain_time(this, 200);
  endtask
endclass

//////////////////////TB//////////////////////
module tb; 
  top_if vif();
    
  apb_mod dut(vif.pclk, 
              vif.presetn, 
              vif.paddr, 
              vif.psel, 
              vif.penable, 
              vif.pwrite, 
              vif.pwdata, 
              vif.pready, 
              vif.prdata, 
              vif.pwakeup, 
              vif.pslverr, 
              vif.enable_O, 
              vif.r_w_O, 
              vif.buad_rate_O, 
              vif.trans_count_O, 
              vif.sl_address_O,
             );  // Added missing connection

  
  initial begin
   vif.pclk <= 0;
  end

  always #10 vif.pclk = ~vif.pclk;
  
  initial begin
    uvm_config_db#(virtual top_if)::set(null, "*", "vif", vif);
    run_test("test");
   end
  
  initial begin
    $dumpfile("dump.vcd");
    $dumpvars;
  end

endmodule
    
         
         
        

The above code is the testbench that I have written for this DUT:

module apb_mod #(parameter BW = 8, CSR_no = 4)(
    ////////  APB signals  ////////
    input wire pclk,                      /// clock
    input wire presetn,                   /// active low synchronous reset
    input wire [BW-1:0] paddr,             /// address
    input wire psel,                       /// peripheral select
    input wire penable,                    /// peripheral enable
    input wire pwrite,                     /// peripheral write
    input wire [BW-1:0] pwdata,            /// peripheral data
    output wire pready,                    /// peripheral ready
    output wire [BW-1:0] prdata,           /// peripheral read data
    input wire pwakeup,                    /// peripheral wakeup
    output wire pslverr,  
        
    //////////  dummy UART CSRs in ping pong //////////
    input wire done,
    output wire enable_O,    
    output wire r_w_O,
    output wire [3:0] buad_rate_O,
    output wire [3:0] trans_count_O,
    output wire [3:0] sl_address_O
);
    
    //////////////  APB REGs  ////////
    reg [2:0] state, next_state;  
    reg pready_reg;
    reg pslverr_reg;
    reg [BW-1:0] prdata_reg;
    
    //////////////////  pingpong Registers  //////
    reg [7:0] s_reg [1:0];                            
    reg [7:0] d_reg_0[3:0];                          
    reg [7:0] d_reg_1[3:0];                          
    
    ///////////////  states of APB  /////////////////////
    localparam IDLE = 3'b000, SETUP = 3'b001, ACCESS = 3'b010;   /// states of APB      
    
    ///////////////////////// uart C S R  regs //////////
    reg enable_O_reg;          
    reg r_w_O_reg;            
    reg [3:0] buad_rate_O_reg;
    reg [3:0] trans_count_O_reg;
    reg [3:0] sl_address_O_reg;
        
    /////////////////////// assignment /////////////////////////////
    assign enable_O      = enable_O_reg;
    assign r_w_O         = r_w_O_reg;
    assign buad_rate_O   = buad_rate_O_reg;  
    assign trans_count_O = trans_count_O_reg;
    assign sl_address_O  = sl_address_O_reg;
    assign prdata        = prdata_reg;
    assign pslverr       = pslverr_reg;
    
    always @(posedge pclk) begin      
        if (!presetn) begin
            s_reg[0][0]      <= 1;      // s_status
            s_reg[0][1]      <= 0;      // producer
            s_reg[0][2]      <= 0;      // consumer
            d_reg_0[0][0]    <= 0;      // enb_0
            d_reg_1[0][0]    <= 0;      // enb_1
            state            <= IDLE;
            pslverr_reg      <= 0;
        end else begin
            state <= next_state;
        end
    end
    
    always @(*) begin
        if (s_reg[0][2] == 0) begin
            enable_O_reg       = d_reg_0[0][1];          
            r_w_O_reg          = d_reg_0[0][2];          
            buad_rate_O_reg    = d_reg_0[1];
            trans_count_O_reg  = d_reg_0[2];
            sl_address_O_reg   = d_reg_0[3];
        end else if (s_reg[0][2] == 1) begin
            enable_O_reg       = d_reg_1[0][1];          
            r_w_O_reg          = d_reg_1[0][2];          
            buad_rate_O_reg    = d_reg_1[1];
            trans_count_O_reg  = d_reg_1[2];
            sl_address_O_reg   = d_reg_1[3];  
        end else begin
            enable_O_reg       = d_reg_0[0][1];          
            r_w_O_reg          = d_reg_0[0][2];          
            buad_rate_O_reg    = d_reg_0[1];
            trans_count_O_reg  = d_reg_0[2];
            sl_address_O_reg   = d_reg_0[3];
        end
    end
    
    always @(posedge pclk) begin
        if (done) begin
            if (d_reg_0[0][0] == 1 & d_reg_1[0][0] == 0) begin
                d_reg_0[0][0] <= 0;
                s_reg[0][2]   <= 0;
            end else if (d_reg_0[0][0] == 0 & d_reg_1[0][0] == 1) begin
                d_reg_1[0][0] <= 0;
                s_reg[0][2]   <= 1;
            end else begin
                s_reg[0][2]   <= 0;                                                            
            end
        end
    end
    
    always @(posedge pclk) begin
        if (penable) begin
            if (pwrite) begin  
                case (s_reg[0][1])
                    1'b0: begin
                        if (paddr == 8'hf0)
                            s_reg[0][1] <= pwdata[0];
                        else begin
                            d_reg_0[paddr]     <= pwdata;
                            s_reg[0][2]        <= 1;
                            s_reg[0][0]        <= 0;
                        end
                    end
                    1'b1: begin
                        if (paddr == 8'hf0)
                            s_reg[0][1] <= pwdata[0];
                        else begin
                            d_reg_1[paddr]     <= pwdata;
                            s_reg[0][2]        <= 0;
                            s_reg[0][0]        <= 0;
                        end
                    end
                    default: begin      
                        d_reg_0[paddr]     <= pwdata;
                        s_reg[0][0]        <= 1;                  
                        pslverr_reg        <= 1;
                    end
                endcase
            end else begin
                prdata_reg <= (paddr[7:4] == 4'hf) ? s_reg[0][2:0] :
                              (paddr[7:4] == 4'he) ? d_reg_0[paddr[3:0]] : d_reg_1[paddr[3:0]];
            end
        end
    end
    
    always @(*) begin    
        case (state)
            IDLE: next_state = psel ? SETUP : IDLE;
            SETUP: next_state = penable ? ACCESS : SETUP;
            ACCESS: next_state = (psel && pready) ? SETUP : (!psel ? IDLE : ACCESS);
            default: next_state = IDLE;
        endcase  
    end
endmodule


//////////////////////Interface//////////////////////
interface top_if ();

    logic   [7 : 0]paddr;        // 8-bit
    logic   [7 : 0]pwdata;       // 8-bit
    logic   [7 : 0]prdata;       // 8-bit
    logic              pwrite;       // 1-bit
    logic              psel;         // 1-bit
    logic              penable;      // 1-bit
    logic              presetn;      // 1-bit
    logic              pclk;         // 1-bit
    logic              pslverr;      // 1-bit
    logic              pwakeup;      // 1-bit
    logic              pready;       // 1-bit
    logic              enable_O;     // 1-bit
    logic              r_w_O;        // 1-bit
  
    logic   [3:0] buad_rate_O;  // 4-bit
    logic   [3:0]trans_count_O;// 4-bit
    logic   [3:0]sl_address_O; // 4-bit

endinterface

I'm not able to read the data (using the read function) that I have written into the environment (using the write function).

The following log shows the output of the program
UVM_INFO testbench.sv(584) @ 0: uvm_test_top \[TEST\] Starting Write Sequence
UVM_INFO testbench.sv(51) @ 30: uvm_test_top.env.agent_inst.d \[DRV\] Mode :Write | WDATA : 187 ADDR : 225
UVM_INFO testbench.sv(135) @ 70: uvm_test_top.env.agent_inst.m \[MON\] Mode : Write | WDATA : 187 ADDR : 225
UVM_INFO testbench.sv(177) @ 70: uvm_test_top.env.s \[SCO\] DATA Stored | Addr : 225 Data :187
-

UVM_INFO testbench.sv(51) @ 90: uvm_test_top.env.agent_inst.d \[DRV\] Mode :Write | WDATA : 187 ADDR : 209
UVM_INFO testbench.sv(588) @ 110: uvm_test_top \[TEST\] Starting Read Sequence
UVM_INFO testbench.sv(135) @ 130: uvm_test_top.env.agent_inst.m \[MON\] Mode : Write | WDATA : 187 ADDR : 209
UVM_INFO testbench.sv(177) @ 130: uvm_test_top.env.s \[SCO\] DATA Stored | Addr : 209 Data :187
-

UVM_INFO testbench.sv(68) @ 150: uvm_test_top.env.agent_inst.d \[DRV\] Mode :Read | WDATA : 187 ADDR : 225 RDATA : x
UVM_INFO testbench.sv(141) @ 190: uvm_test_top.env.agent_inst.m \[MON\] Mode : Read | WDATA : 187 ADDR : 225 RDATA : x
UVM_ERROR testbench.sv(187) @ 190: uvm_test_top.env.s \[SCO\] Test Failed -\> Addr : 225 Data :187
-

UVM_INFO testbench.sv(68) @ 210: uvm_test_top.env.agent_inst.d \[DRV\] Mode :Read | WDATA : 187 ADDR : 209 RDATA : x
UVM_INFO testbench.sv(141) @ 250: uvm_test_top.env.agent_inst.m \[MON\] Mode : Read | WDATA : 187 ADDR : 209 RDATA : x
UVM_ERROR testbench.sv(187) @ 250: uvm_test_top.env.s \[SCO\] Test Failed -\> Addr : 209 Data :187
-

Here I'm expecting the data to be read and show it as "Test Passed", but the data written isn't found in the prdata register at all. What could be the problem, and how do I fix it?


Solution

  • Since you named the DUT module apb_mod, I am assuming your goal is for the design to act as an AMBA APB slave and the testbench as an APB master.

    If that is the case, then there are a few mistakes that I can see by inspection:

    • The DUT should drive the pready output, but it does not. The APB protocol requires the design to drive this signal for all transactions. In simulation, it will always be z.
    • The testbench must sample pready for both read and write transactions, but it does not.
    • The transaction class declares prdata as a 2-state type (bit) which is assigned to a 4-state type (logic) in the driver class. You should add code to the driver to display a message (either a uvm_info or a uvm_error) when this conversion from x/z to 0 occurs.
    • It is misleading to display WDATA for a "Read" transaction because the write data is irrelevant. In the monitor and driver, it is much more useful to display the transaction data instead of the interface data. It is good practice to add a display function to the transaction class, then just call that in the monitor and driver.

    You should debug the problem using waveforms; it is insufficient to rely solely on the log output to debug the problem. This will narrow your problem down to either a DUT or a testbench bug. In waveforms:

    • Prove that the write occurred on the apb_mod module port signals.
    • If that looks good, prove that the register was written with the correct data. If not, then there is a design bug.

    If you update the question with your link to EDA Playground, you will get more detailed help. That is far too much code for anyone to copy and get running.


    Now that you updated the question with your link to EDA Playground, I can run the simulation. In waveforms, I see that apb_mod.prdata is unknown (x) for the whole simulation. This is the reason you see "RDATA : x" in your log file.

    I also see that apb_mod.presetn is 1 for the whole simulation. This means that the testbench did not properly reset the design. You need to start the simulation with presetn=0, then after a couple clock cycles, set it to 1.

    In your driver, create a reset task. Also create a reset sequence and call it before your write sequence.