-1

I'm trying to learn UVM in SystemVerilog. I understand the very ideology of UVM, but I have difficulty writing a working case.
I'm trying to write an apb testbench. It compiles and runs, which is already a victory for me, but the transactions received by the monitor have zero values and are non-stop.

I was looking for information on this issue and came to the conclusion that the problem is related to raise / drop objection.

Here is the test-class code, contains this calls

`include "uvm_macros.svh"
import uvm_pkg::*;
class apb_test extends uvm_test;
    `uvm_component_utils(apb_test);
    apb_env env;
    function new(string name, uvm_component parent);
        super.new(name,parent);
    endfunction: new

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        begin
            apb_configuration apb_cfg;
            apb_cfg = new;
            assert(apb_cfg.randomize());
            uvm_config_db#(apb_configuration)::set(.cntxt(this), .inst_name("*"), .field_name("config"), .value(apb_cfg) );
            env = apb_env::type_id::create(.name("env"), .parent(this));
        end
    endfunction: build_phase

    task run_phase(uvm_phase phase);
        apb_sequence apb_seq;
        phase.raise_objection(.obj(this));
        apb_seq = apb_sequence::type_id::create(.name("apb_seq"));
        //assert(apb_seq.randomize());
        `uvm_info("apb_test", {"\n",apb_seq.sprint()}, UVM_LOW)
        apb_seq.start(env.agent.apb_seq);
        #10ns;
        phase.drop_objection(.obj(this));
    endtask: run_phase
endclass: apb_test

The whole project:

//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 30.04.2019 17:12:58
// Design Name:
// Module Name: apb_interface
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////

`include "uvm_macros.svh"
interface apb_if #(  ADDR_WIDTH = 3
            ,  SEL_WIDTH = 2
            ,WRITE_WIDTH = 32
            , READ_WIDTH = WRITE_WIDTH
            )
            (input bit clk, reset_n);

    localparam STRB_WIDTH = WRITE_WIDTH%8? (WRITE_WIDTH/8)+1 : WRITE_WIDTH/8;


    logic [ADDR_WIDTH:0]    addr;
    logic [ 2:0]            prot;
    logic [SEL_WIDTH-1:0]   sel;
    logic                   enable;
    logic                   write;
    logic [WRITE_WIDTH-1:0] wdata;
    logic [STRB_WIDTH-1:0]  strb;
    logic                   ready;
    logic [READ_WIDTH-1:0]  rdata;
    logic                   slv_err;

    clocking master_cb @ (posedge clk);
        default input #1ns output #1ns;
        output addr, prot, sel, enable, write, wdata, strb;
        input ready, rdata, slv_err;
    endclocking: master_cb

    clocking slave_cb @(posedge clk);
        default input #1ns output #1ns;
        input addr, prot, sel, enable, write, wdata, strb;
        output ready, rdata, slv_err;
    endclocking: slave_cb

    clocking monitor_cb @(posedge clk);
        default input #1ns output #1ns;
        input addr, prot, sel, enable, write, wdata, strb, ready, rdata, slv_err;
    endclocking: monitor_cb

    modport master_mp (input clk, reset_n, ready, rdata, slv_err, output addr, prot, sel, enable, write, wdata, strb);
    modport slave_mp  (input clk, reset_n, addr, prot, sel, enable, write, wdata, strb, output ready, rdata, slv_err);
    modport master_sync_mp (clocking master_cb);
    modport slave_sync_mp  (clocking slave_cb );

endinterface: apb_if

package apb_pkg;
    import uvm_pkg::*;
    class apb_configuration extends uvm_object;
       `uvm_object_utils( apb_configuration )

       function new( string name = "" );
          super.new( name );
       endfunction: new
    endclass: apb_configuration
    class apb_seq_item #(  ADDR_WIDTH = 3
                ,  SEL_WIDTH = 2
                ,WRITE_WIDTH = 32
                , READ_WIDTH = WRITE_WIDTH
                ) extends uvm_sequence_item;
        localparam STRB_WIDTH = WRITE_WIDTH%8? (WRITE_WIDTH/8)+1 : WRITE_WIDTH/8;
        `uvm_object_utils(apb_seq_item)

        // Control information
        rand bit [31:0]            addr;
        rand bit [ 2:0]            prot;
        rand bit [SEL_WIDTH-1:0]   sel;
        rand bit                   write;
        rand bit                   ready;
        // Payload information
        rand bit [WRITE_WIDTH-1:0] wdata;
        rand bit [STRB_WIDTH-1:0]  strb;
        // Analysis information
        rand bit [READ_WIDTH-1:0]  rdata;
        rand bit                   slv_err;
        constraint read_constr {
            write == 0 -> strb == 0;
        }
        function new(string name = "apb_seq_item");
            super.new(name);
        endfunction: new

        virtual function void do_copy(uvm_object rhs);
            apb_seq_item rhs_;
            if(!$cast(rhs_, rhs)) begin
              uvm_report_error("do_copy:", "Cast failed");
              return;
            end
            super.do_copy(rhs); // Chain the copy with parent classes
            addr    = rhs_.addr;
            prot    = rhs_.prot;
            sel     = rhs_.sel;
            write   = rhs_.write;
            ready   = rhs_.ready;
            wdata   = rhs_.wdata;
            strb    = rhs_.strb;
            rdata   = rhs_.rdata;
            slv_err = rhs_.slv_err;
        endfunction: do_copy

        virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer);
            apb_seq_item rhs_;
            // If the cast fails, comparison has also failed
             // A check for null is not needed because that is done in the compare()
             // function which calls do_compare()
             if(!$cast(rhs_, rhs)) begin
               return 0;
             end
             return( super.do_compare(rhs,comparer) &&
                    (addr   = rhs_.addr)    &&
                    (prot   = rhs_.prot)    &&
                    (sel    = rhs_.sel)     &&
                    (write  = rhs_.write)   &&
                    (ready  = rhs_.ready)   &&
                    (wdata  = rhs_.wdata)   &&
                    (strb   = rhs_.strb)    &&
                    (rdata  = rhs_.rdata)   &&
                    (slv_err= rhs_.slv_err));
        endfunction: do_compare

        virtual function string convert2string();
            string s;

            s = super.convert2string();
            // Note the use of \t (tab) and \n (newline) to format the data in columns
            // The enumerated op_code types .name() method returns a string corresponding to its value
            s = {s, $psprintf("\naddr\t\t: %0h",addr)};
            s = {s, $psprintf("\nprot\t\t: %0b",prot)};
            s = {s, $psprintf("\nsel\t\t: %0b",sel)};
            s = {s, $psprintf("\nwrite\t\t: %0b",write)};
            s = {s, $psprintf("\nready\t\t: %0b",ready)};
            s = {s, $psprintf("\nwdata\t\t: %0h",wdata)};
            s = {s, $psprintf("\nstrb\t\t: %0b",strb)};
            s = {s, $psprintf("\nrdata\t\t: %0h",rdata)};
            s = {s, $psprintf("\nslv_err\t: %0b",slv_err)};
            return s;
        endfunction: convert2string

        virtual function void do_print(uvm_printer printer);
            $display(convert2string());
        endfunction: do_print

        // This implementation is simulator specific.
        // In order to get transaction viewing to work with Questa you need to
        // Set the recording_detail config item to UVM_FULL:
        // set_config_int("*", "recording_detail", UVM_FULL);
        virtual function void do_record(uvm_recorder recorder);
            super.do_record(recorder); // To record any inherited data members
            `uvm_record_field("addr", addr)
            `uvm_record_field("prot", prot)
            `uvm_record_field("sel", sel)
            `uvm_record_field("write", write)
            `uvm_record_field("ready", ready)
            `uvm_record_field("wdata", wdata)
            `uvm_record_field("strb", strb)
            `uvm_record_field("rdata", rdata)
            `uvm_record_field("slv_err", slv_err)
        endfunction: do_record

        virtual function void do_pack(uvm_packer packer);
            super.do_pack(packer);
            `uvm_pack_int(addr);
            `uvm_pack_int(prot);
            `uvm_pack_int(sel);
            `uvm_pack_int(write);
            `uvm_pack_int(ready);
            `uvm_pack_int(wdata);
            `uvm_pack_int(strb);
            `uvm_pack_int(rdata);
            `uvm_pack_int(slv_err);
        endfunction: do_pack

        virtual function void do_unpack(uvm_packer packer);
            super.do_unpack(packer);
            `uvm_unpack_int(addr);
            `uvm_unpack_int(prot);
            `uvm_unpack_int(sel);
            `uvm_unpack_int(write);
            `uvm_unpack_int(ready);
            `uvm_unpack_int(wdata);
            `uvm_unpack_int(strb);
            `uvm_unpack_int(rdata);
            `uvm_unpack_int(slv_err);
        endfunction: do_unpack

    endclass: apb_seq_item

    class apb_sequence extends uvm_sequence#(apb_seq_item);
        `uvm_object_utils(apb_sequence)

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

        task body();
            apb_seq_item trans;
            repeat(2) begin
                trans = apb_seq_item#()::type_id::create("ap_it");
                start_item(trans);
                assert(req.randomize());
                finish_item(trans);
            end
        endtask: body
    endclass: apb_sequence

    class apb_sequencer extends uvm_sequencer#(apb_seq_item);
        `uvm_component_utils(apb_sequencer)
        function new(string name, uvm_component parent = null);
            super.new(name, parent);
        endfunction: new
    endclass: apb_sequencer

    class apb_driver extends uvm_driver#(apb_seq_item);
        `uvm_component_utils(apb_driver)
        virtual apb_if apb_vi;
        function new (string name, uvm_component parent);
            super.new(name, parent);
        endfunction: new

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            void'(uvm_resource_db#(virtual apb_if)::read_by_name
            (.scope("ifs"), .name("apb_if"), .val(apb_vi) ) );
        endfunction: build_phase

        task run_phase(uvm_phase phase);
            apb_seq_item trans;
            //super.run_phase(phase);
            apb_vi.master_cb.sel    <= 0;
            apb_vi.master_cb.enable <= 1'b0;
            forever begin
                seq_item_port.get_next_item(trans);
                uvm_report_info("APB_DRIVER ", $psprintf("Got Transaction %s",trans.convert2string()));
                @apb_vi.master_cb;
                apb_vi.master_cb.addr   <= trans.addr;
                apb_vi.master_cb.sel    <= trans.sel;
                apb_vi.master_cb.prot   <= trans.prot;
                if(trans.write)begin
                    apb_vi.master_cb.write  <= 1'b1;
                    //apb_vi.master_cb.wdata  <= trans.wdata;
                    apb_vi.master_cb.strb   <= trans.strb;
                    @apb_vi.master_cb;
                    apb_vi.master_cb.enable <= 1'b1;
                    // while(!apb_vi.master_cb.ready)begin
                    //     @apb_vi.master_cb;
                    // end
                end
                else begin
                    apb_vi.master_cb.write  <= 1'b0;
                    @apb_vi.master_cb;
                    apb_vi.master_cb.enable <= 1'b1;
                    // while(!apb_vi.master_cb.ready)begin
                    //     @apb_vi.master_cb;
                    // end
                    trans.rdata     <= apb_vi.master_cb.rdata;
                    trans.slv_err   <= apb_vi.master_cb.slv_err;
                end
                apb_vi.master_cb.sel    <= 0;
                apb_vi.master_cb.enable <= 1'b0;
                seq_item_port.item_done();
            end
        endtask: run_phase
    endclass: apb_driver
    class apb_monitor extends uvm_monitor;
        `uvm_component_utils(apb_monitor);
        uvm_analysis_port#(apb_seq_item) apb_ap;
        virtual apb_if apb_vi;

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

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            void'(uvm_resource_db#(virtual apb_if)::read_by_name(.scope("ifs"), .name("apb_if"), .val(apb_vi) ) );
            apb_ap = new(.name("apb_ap"), .parent(this));
        endfunction: build_phase

        task run_phase(uvm_phase phase);
            forever begin
                apb_seq_item trans;
                trans = apb_seq_item#()::type_id::create(.name("trans"));
                trans.addr      <= apb_vi.monitor_cb.addr;
                trans.prot      <= apb_vi.monitor_cb.prot;
                trans.sel       <= apb_vi.monitor_cb.sel;
                trans.write     <= apb_vi.monitor_cb.write;
                trans.wdata     <= apb_vi.monitor_cb.wdata;
                trans.strb      <= apb_vi.monitor_cb.strb;
                trans.rdata     <= apb_vi.monitor_cb.rdata;
                trans.slv_err   <= apb_vi.monitor_cb.slv_err;
                uvm_report_info("APB_MONITOR", $psprintf("Got Transaction %s", trans.convert2string()));
                apb_ap.write(trans);
            end
        endtask: run_phase
    endclass: apb_monitor
    class apb_agent extends uvm_agent;
        `uvm_component_utils(apb_agent)
        uvm_analysis_port#(apb_seq_item) apb_ap;

        apb_sequencer   apb_seq;
        apb_driver      apb_drv;
        apb_monitor     apb_mon;

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

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);

            apb_ap  = new(.name("apb_ap"), .parent(this));
            apb_seq = apb_sequencer ::type_id::create(.name("apb_seq"), .parent(this) );
            apb_drv = apb_driver    ::type_id::create(.name("apb_drv"), .parent(this) );
            apb_mon = apb_monitor   ::type_id::create(.name("apb_mon"), .parent(this) );
        endfunction: build_phase

        function void connect_phase(uvm_phase phase);
            super.connect_phase(phase);
            apb_drv.seq_item_port.connect(apb_seq.seq_item_export);
            apb_mon.apb_ap.connect(apb_ap);
        endfunction: connect_phase
    endclass: apb_agent
    class apb_fc_subs#(  ADDR_WIDTH = 3
                ,  SEL_WIDTH = 2
                ,WRITE_WIDTH = 32
                , READ_WIDTH = WRITE_WIDTH
                ) extends uvm_subscriber#(apb_seq_item);
        `uvm_component_utils(apb_fc_subs);
        apb_seq_item trans;
        covergroup apb_cg;
            address: coverpoint trans.addr {
                bins low    = {0, 1 << ADDR_WIDTH/4 - 1};
                bins med    = {1 << ADDR_WIDTH/4, 1 << ADDR_WIDTH/2 - 1};
                bins high   = {1 << ADDR_WIDTH/2, 1 << ADDR_WIDTH - 1};
            }
            select: coverpoint trans.sel;
            prot:   coverpoint trans.prot;
            wdata:  coverpoint trans.wdata;// iff(write);
            strb:   coverpoint trans.strb;// iff(write);
            rw:  coverpoint trans.write{
                bins read   = {0};
                bins write  = {1};
            }
        endgroup: apb_cg

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

        function void write(apb_seq_item t);
            trans = t;
            apb_cg.sample();
        endfunction: write
    endclass: apb_fc_subs

    typedef class apb_scoreboard;
    class apb_sb_subs extends uvm_subscriber#(apb_seq_item);
        `uvm_component_utils(apb_sb_subs)

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

        function void write(apb_seq_item t);
            apb_scoreboard apb_sb;
            $cast(apb_sb, m_parent);
            apb_sb.apb_check(t);
        endfunction: write
    endclass: apb_sb_subs
    class apb_scoreboard extends uvm_scoreboard;
        `uvm_component_utils(apb_scoreboard)

        uvm_analysis_export#(apb_seq_item) apb_analysis_export;

        local apb_sb_subs sb_sub;

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

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            apb_analysis_export = new(.name("apb_analysis_export"), .parent(this));
            sb_sub = apb_sb_subs::type_id::create(.name("sb_sub"), .parent(this));
        endfunction: build_phase

        function void connect_phase(uvm_phase phase);
            super.connect_phase(phase);
            apb_analysis_export.connect(sb_sub.analysis_export);
        endfunction: connect_phase

        virtual function void apb_check(apb_seq_item trans);
            `uvm_info("SCOREBOARD","Dummy check",UVM_LOW);
        endfunction: apb_check
    endclass: apb_scoreboard
    class apb_env extends uvm_env;
        `uvm_component_utils(apb_env)

        apb_agent       agent;
        apb_fc_subs     fc_sub;
        apb_scoreboard  sco;

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

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            agent   = apb_agent     ::type_id::create(.name("agent")    , .parent(this));
            fc_sub  = apb_fc_subs#()::type_id::create(.name("fc_sub")   , .parent(this));
            sco     = apb_scoreboard::type_id::create(.name("sco")      , .parent(this));
        endfunction: build_phase

        function void connect_phase(uvm_phase phase);
            super.connect_phase(phase);
            agent.apb_ap.connect(fc_sub.analysis_export);
            agent.apb_ap.connect(sco.apb_analysis_export);
        endfunction: connect_phase
    endclass: apb_env
    class apb_test extends uvm_test;
        `uvm_component_utils(apb_test);
        apb_env env;
        function new(string name, uvm_component parent);
            super.new(name,parent);
        endfunction: new

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            begin
                apb_configuration apb_cfg;
                apb_cfg = new;
                assert(apb_cfg.randomize());
                uvm_config_db#(apb_configuration)::set(.cntxt(this), .inst_name("*"), .field_name("config"), .value(apb_cfg) );
                env = apb_env::type_id::create(.name("env"), .parent(this));
            end
        endfunction: build_phase

        task run_phase(uvm_phase phase);
            apb_sequence apb_seq;
            phase.raise_objection(.obj(this));
            apb_seq = apb_sequence::type_id::create(.name("apb_seq"));
            //assert(apb_seq.randomize());
            `uvm_info("apb_test", {"\n",apb_seq.sprint()}, UVM_LOW)
            apb_seq.start(env.agent.apb_seq);
            #10ns;
            phase.drop_objection(.obj(this));
        endtask: run_phase
    endclass: apb_test
endpackage: apb_pkg
module apb_memory #( parameter ADDR_WIDTH
                    ,parameter SEL_WIDTH
                    ,parameter WRITE_WIDTH
                    ,parameter READ_WIDTH
                   )
                   (apb_if.slave_mp apb_intf);
    localparam IDLE = 2'd0;
    localparam WRITE= 2'd1;
    localparam READ = 2'd2;

    bit [1:0] state;

    bit [ADDR_WIDTH-1:0]addr;
    bit [WRITE_WIDTH-1:0]data[1<<ADDR_WIDTH];
    initial begin
        state = 0;
    end

    always@(apb_intf.clk) begin
    if(!apb_intf.reset_n) state <= IDLE;
    else
        case(state)
        IDLE:   if(apb_intf.sel != 0) begin
                    if(apb_intf.write)begin
                        state <= WRITE;
                    end
                    else begin
                        state <= READ;
                        apb_intf.rdata <= data[apb_intf.addr];
                    end
                    apb_intf.ready <= 1;
                end
        WRITE:  if(apb_intf.enable) begin
                    data[apb_intf.addr] <= apb_intf.wdata;
                    apb_intf.ready <= 0;
                    state <= IDLE;
                end
        READ:   if(apb_intf.enable) begin
                    apb_intf.slv_err <= 0;
                    state <= IDLE;
                end
        default: state <= IDLE;
        endcase
    end
endmodule
module test_bench;
    import uvm_pkg::*;
    import apb_pkg::*;
    parameter ADDR_WIDTH    = 3;
    parameter SEL_WIDTH     = 2;
    parameter WRITE_WIDTH   = 32;
    parameter READ_WIDTH    = WRITE_WIDTH;
    bit clk,reset;
    apb_if #(  ADDR_WIDTH
                ,  SEL_WIDTH
                ,WRITE_WIDTH
                , READ_WIDTH
                )apb_intf(clk,~reset);
    apb_memory #(  ADDR_WIDTH
                ,  SEL_WIDTH
                ,WRITE_WIDTH
                , READ_WIDTH
                )DUT(apb_intf);
    initial begin
        clk = 0;
       #5ns ;
       forever #5ns clk = ! clk;
    end

    initial begin
       uvm_resource_db#( virtual apb_if )::set
     ( .scope( "ifs" ), .name( "apb_if" ), .val( apb_intf ) );
       run_test();
    end
endmodule

or placed here: https://github.com/HepoH3/apb_testbench

1 Answers1

1

The problem is with your monitor—you have a forever loop with zero delay. You at least want a clock cycle delay. And then you should only perform writes when there is a valid operation. (i.e. enable is true.

Then the next problem is with your driver. You have not properly timed the driving of all your signals. You really need to learn how to display output from your simulation by dumping waves to view, or print info messages.

dave_59
  • 39,096
  • 3
  • 24
  • 63