一个加法器的验证

1.DUT

module alu_top (
    input   wire            clk_i,
    input   wire            rst_i,
    input   wire    [31:0]  dataA_i,
    input   wire    [31:0]  dataB_i,
    input   wire    [ 2:0]  ALUCtrl_i,     // Operation code
    output  logic   [31:0]  ALUResult_o,
    output  logic           Zero_o
);

reg [31:0] temp_alu_result;
reg temp_zero;

always @(*) begin
    if (rst_i == 1'b1) begin
        temp_alu_result =  32'b0;
        temp_zero      =   1'b0;
    end else begin
        case (ALUCtrl_i)
            3'b010: // ADD
            begin
                temp_alu_result = dataA_i + dataB_i;
            end

            3'b110: // SUB
            begin
                temp_alu_result = dataA_i - dataB_i;
            end

            3'b000: // AND
            begin
                temp_alu_result = dataA_i & dataB_i;
            end

            3'b001: // OR
            begin
                temp_alu_result = dataA_i | dataB_i;
            end

            3'b011: // XOR
            begin
                temp_alu_result = dataA_i ^ dataB_i;
            end

            3'b100: // NOR
            begin
                temp_alu_result = ~(dataA_i | dataB_i);
            end

            3'b111: // SLT
            begin
                if ((dataA_i - dataB_i) < 0)
                    temp_alu_result = 32'b1;
                else
                    temp_alu_result = 32'b0;
            end

            default:
            begin
                temp_alu_result =  32'b0;
                temp_zero      =   1'b0;
            end
        endcase

        // The zero signal is mainly used by the branch
        if (temp_alu_result == 32'b0) begin
            temp_zero = 1'b1;
        end else begin
            temp_zero = 1'b0;
        end
    end // NOT reset

end // always

always @(posedge clk_i or posedge rst_i) begin
    if (rst_i == 1'b1) begin
        ALUResult_o <= 32'b0;
        Zero_o <= 1'b1;
    end else begin
        ALUResult_o <= temp_alu_result;
        Zero_o <= temp_zero;
    end
end // always

endmodule : alu_top

2.transaction

`ifndef ALU_BASE_SEQ_ITEM__SV
`define ALU_BASE_SEQ_ITEM__SV

class alu_base_seq_item extends uvm_sequence_item;

    rand bit [31:0] dataA_i;
    rand bit [31:0] dataB_i;
    randc bit [ 2:0] ALUCtrl_i;
    bit [31:0] ALUResult_o;
    bit        Zero_o;

    rand int wait_cycles;

    `uvm_object_utils_begin(alu_base_seq_item)
        `uvm_field_int(dataA_i, UVM_DEFAULT)
        `uvm_field_int(dataB_i, UVM_DEFAULT)
        `uvm_field_int(ALUCtrl_i, UVM_DEFAULT)
        `uvm_field_int(ALUResult_o, UVM_DEFAULT)
        `uvm_field_int(Zero_o, UVM_DEFAULT)
    `uvm_object_utils_end

    //------------------------------------------
    // Constraints
    //------------------------------------------
    constraint delay_loops {wait_cycles inside {[0:5]};}
    constraint valid_alu_op {ALUCtrl_i inside {3'b000, 3'b001, 3'b010, 3'b011, 3'b100, 3'b110, 3'b111};}

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

endclass: alu_base_seq_item

`endif

3.sequence

`ifndef ALU_BASE_SEQ__SV
`define ALU_BASE_SEQ__SV

class alu_base_seq extends uvm_sequence#(alu_base_seq_item);
    `uvm_object_utils(alu_base_seq)

    // Each sequence has a random number of operations.
    // There is a constraint to keep the number reasonable.
    rand int unsigned num_of_ops;
    constraint num_of_ops_con {num_of_ops inside {[16:24]};}

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

    // This task generates a sequence of transactions.
    task body();
        alu_base_seq_item m_alu_base_seq_item_h;
        repeat (num_of_ops) begin
            m_alu_base_seq_item_h = alu_base_seq_item::type_id::create(.name("m_alu_base_seq_item_h"));
            start_item(m_alu_base_seq_item_h);
            if (!m_alu_base_seq_item_h.randomize()) begin
                `uvm_error("m_alu_base_seq_item_h", "Randomize failed.");
            end
            finish_item(m_alu_base_seq_item_h);
        end
    endtask: body
endclass: alu_base_seq
`endif

 4.sequencer

直接使用参数化的uvm_sequencer

5.interface

`ifndef ALU_IF__SV
`define ALU_IF__SV

interface alu_if (input bit clk);

    // ALU signals
    logic [31:0] dataA_i, dataB_i, ALUResult_o;
    logic [2:0]  ALUCtrl_i;
    logic rst_i, Zero_o;

endinterface : alu_if

`endif

6.driver

`ifndef ALU_DRIVER__SV
`define ALU_DRIVER__SV

class alu_driver extends uvm_driver#(alu_base_seq_item);
    `uvm_component_utils(alu_driver)

    virtual alu_if m_alu_vif_h;

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

    // In the build phase, the driver is connected to the virtual interface.
    // The DUT is also connected to the same interface.
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        assert(uvm_config_db#(virtual alu_if)::get
        (.cntxt(this), .inst_name(""), .field_name("alu_if"), .value(m_alu_vif_h)));
    endfunction: build_phase

    task run_phase(uvm_phase phase);
        alu_base_seq_item m_req_h;

        m_alu_vif_h.dataA_i   <= 32'b0;
        m_alu_vif_h.dataB_i   <= 32'b0;
        m_alu_vif_h.ALUCtrl_i <= 3'b0;

        // Generate a reset sequence.
        // FIXME: Move this to the pre-run phase.
        m_alu_vif_h.rst_i     <= 1'b1;
        repeat (2) @(posedge m_alu_vif_h.clk);
        m_alu_vif_h.rst_i     <= 1'b0;
        // Keep receiving the next transaction from the sequence until you run out.
        // Drive each transaction on the interface as per the protocol.

        // In the alu_agent, the seq_item_port of the alu_driver is hooked up to the
        // seq_item_export port on the sequencer (alu_base_seq_item).
        // The alu_driver accepts alu_transactions with the get_next_item function.

        // Sample timing diagram
        //          ___     ___     ___
        // CLK  ___|   |___|   |___|   |___
        //
        // INP  _______########------------
        //
        // OUT  -------------------#####----
        forever begin
            seq_item_port.get_next_item(m_req_h);

            `uvm_info("DRIVER_START:", $sformatf("DATA:: dataA_i: %32x, dataB_i: %32x, ALU_OP: %3b", m_req_h.dataA_i, m_req_h.dataB_i, m_req_h.ALUCtrl_i), UVM_DEBUG)
            @(negedge m_alu_vif_h.clk);
            m_alu_vif_h.dataA_i   = m_req_h.dataA_i;
            m_alu_vif_h.dataB_i   = m_req_h.dataB_i;
            m_alu_vif_h.ALUCtrl_i = m_req_h.ALUCtrl_i;
            @(negedge m_alu_vif_h.clk);
            m_alu_vif_h.dataA_i   = 32'bz;
            m_alu_vif_h.dataB_i   = 32'bz;
            m_alu_vif_h.ALUCtrl_i = 3'bz;
            `uvm_info("DRIVER_DONE:", $sformatf("DATA:: dataA_i: %32x, dataB_i: %32x, ALU_OP: %3b", m_alu_vif_h.dataA_i, m_alu_vif_h.dataB_i, m_alu_vif_h.ALUCtrl_i), UVM_DEBUG)
            `uvm_info("DRIVER_WAIT:", $sformatf("Waiting for %1d cycles", m_req_h.wait_cycles), UVM_DEBUG)
            repeat (m_req_h.wait_cycles) @(negedge m_alu_vif_h.clk);
            seq_item_port.item_done();
        end // forever
    endtask: run_phase
endclass: alu_driver
`endif

7.monitor

`ifndef ALU_MONITOR__SV
`define ALU_MONITOR__SV

class alu_monitor extends uvm_monitor;
    `uvm_component_utils(alu_monitor)

    virtual alu_if m_alu_vif_h;
    uvm_analysis_port #(alu_base_seq_item) m_analysis_port_h;

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

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        m_analysis_port_h = new("m_analysis_port_h", this);
    endfunction: build_phase

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        assert(uvm_config_db#(virtual alu_if)::get
        (.cntxt(this), .inst_name(""), .field_name("alu_if"), .value(m_alu_vif_h)));
    endfunction: connect_phase

    task run_phase(uvm_phase phase);
        alu_base_seq_item m_req_h = alu_base_seq_item::type_id::create("monitor_m_req_h");
        alu_base_seq_item m_req_clone_h;
        @(negedge m_alu_vif_h.rst_i);
        forever begin
            @(posedge m_alu_vif_h.clk);
            m_req_h.dataA_i  = m_alu_vif_h.dataA_i;
            m_req_h.dataB_i  = m_alu_vif_h.dataB_i;
            m_req_h.ALUCtrl_i = m_alu_vif_h.ALUCtrl_i;
            `uvm_info("MONITOR:", $sformatf("DATA:: dataA_i: %32x, dataB_i: %32x, ALU_OP: %3b", m_req_h.dataA_i, m_req_h.dataB_i, m_req_h.ALUCtrl_i), UVM_DEBUG)
            @(posedge m_alu_vif_h.clk);
            m_req_h.ALUResult_o = m_alu_vif_h.ALUResult_o;
            m_req_h.Zero_o = m_alu_vif_h.Zero_o;
            `uvm_info("MONITOR:", $sformatf("RESULT:: ALUResult_o: %32x, Zero_o: %1x", m_req_h.ALUResult_o, m_req_h.Zero_o), UVM_DEBUG)
            $cast(m_req_clone_h, m_req_h.clone());
            m_analysis_port_h.write(m_req_clone_h);
        end // forever
    endtask: run_phase
endclass: alu_monitor
`endif

8.agent

`ifndef ALU_AGENT__SV
`define ALU_AGENT__SV

class alu_agent extends uvm_agent;
    `uvm_component_utils(alu_agent)

    uvm_analysis_port #(alu_base_seq_item) m_analysis_port_h;
    uvm_sequencer #(alu_base_seq_item)     m_sequencer_h;
    alu_driver                             m_driver_h;
    alu_monitor                            m_monitor_h;

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

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

        // Monitor should always be present.
        m_monitor_h = alu_monitor::type_id::create("m_monitor_h", this);

        // Analysis port should always be present.
        m_analysis_port_h = new("m_analysis_port_h", this);

        // Only build the driver and sequencer if agent is active.
        m_driver_h = alu_driver::type_id::create("m_driver_h", this);
        m_sequencer_h = uvm_sequencer#(alu_base_seq_item)::type_id::create("m_sequencer_h", this);

    endfunction: build_phase

    virtual function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        m_monitor_h.m_analysis_port_h.connect(m_analysis_port_h);

        // Only connect the driver and the sequencer if agent is active.
        m_driver_h.seq_item_port.connect(m_sequencer_h.seq_item_export);
    endfunction: connect_phase
endclass: alu_agent
`endif

9.monitor

`ifndef SCOREBOARD__SV
`define SCOREBOARD__SV
class scoreboard extends uvm_component;

    `uvm_component_utils(scoreboard)

    uvm_tlm_analysis_fifo #(alu_base_seq_item) m_alu_fifo_h;

    function new(string name = "scoreboard", uvm_component parent = null);
        super.new(name, parent);
    endfunction: new

    function void build_phase(uvm_phase phase);
        m_alu_fifo_h = new("m_alu_fifo_h", this);
    endfunction: build_phase

    task run_phase(uvm_phase phase);

        alu_base_seq_item alu_op;
        bit [31:0] expALUResult_o;
        bit        expZero_o;
        forever begin
            m_alu_fifo_h.get(alu_op);
            `uvm_info("SCOREBOARD:", $sformatf("DATA:: alu_op.dataA_i: %32x, alu_op.dataB_i: %32x, ALU_OP: %3b", alu_op.dataA_i, alu_op.dataB_i, alu_op.ALUCtrl_i), UVM_NONE)

            case (alu_op.ALUCtrl_i)
                3'b010: // ADD
                begin
                    expALUResult_o = alu_op.dataA_i + alu_op.dataB_i;
                end

                3'b110: // SUB
                begin
                    expALUResult_o = alu_op.dataA_i - alu_op.dataB_i;
                end

                3'b000: // AND
                begin
                    expALUResult_o = alu_op.dataA_i & alu_op.dataB_i;
                end

                3'b001: // OR
                begin
                    expALUResult_o = alu_op.dataA_i | alu_op.dataB_i;
                end

                3'b011: // XOR
                begin
                    expALUResult_o = alu_op.dataA_i ^ alu_op.dataB_i;
                end

                3'b100: // NOR
                begin
                    expALUResult_o = ~(alu_op.dataA_i | alu_op.dataB_i);
                end

                3'b111: // SLT
                begin
                    if ((alu_op.dataA_i - alu_op.dataB_i) < 0)
                        expALUResult_o = 32'b1;
                    else
                        expALUResult_o = 32'b0;
                end

                default:
                begin
                    expALUResult_o =  32'b0;
                    expZero_o      =   1'b0;
                end
            endcase
            // The zero signal is mainly used by the branch
            if (expALUResult_o == 32'b0) begin
                expZero_o = 1'b1;
            end else begin
                expZero_o = 1'b0;
            end

            `uvm_info("SCOREBOARD:", $sformatf("DATA:: alu_op.ALUResult_o: %32x, expALUResult_o: %32x, alu_op.Zero_o: %1b, expZero_o: %1b", alu_op.ALUResult_o, expALUResult_o, alu_op.Zero_o, expZero_o), UVM_NONE)
            if(alu_op.ALUResult_o == expALUResult_o && alu_op.Zero_o == expZero_o) begin
                `uvm_info("SCOREBOARD:", $sformatf("CHECK_OK"), UVM_NONE)
            end else begin
                `uvm_error("SCOREBOARD:", $sformatf("CHECK_FAILED"))
            end

        end // forever
    endtask: run_phase

endclass: scoreboard
`endif

 10.env

`ifndef ENV__SV
`define ENV__SV

class alu_env extends uvm_env;

    `uvm_component_utils(alu_env)

    alu_agent m_alu_agent_h;
    scoreboard m_scoreboard_h;

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

    function void build_phase(uvm_phase phase);
        m_alu_agent_h = alu_agent::type_id::create("m_alu_agent_h", this);
        m_scoreboard_h = scoreboard::type_id::create("m_scoreboard_h", this);
    endfunction: build_phase

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        m_alu_agent_h.m_analysis_port_h.connect(m_scoreboard_h.m_alu_fifo_h.analysis_export);
    endfunction: connect_phase

endclass: alu_env
`endif

 11.testcase

`ifndef ALU_BASE_TEST__SV
`define ALU_BASE_TEST__SV

class alu_base_test extends uvm_test;
    `uvm_component_utils(alu_base_test)

    alu_env m_alu_env_h;

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

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        m_alu_env_h = alu_env::type_id::create(.name("m_alu_env_h"), .parent(this));
    endfunction: build_phase

    task run_phase(uvm_phase phase);
        alu_base_seq m_alu_seq_h;

        // Raise objection so the test will not end. Dropping it will end the test.
        phase.raise_objection(.obj(this));
        m_alu_seq_h = alu_base_seq::type_id::create(.name("m_alu_seq_h"));
        assert(m_alu_seq_h.randomize());
        `uvm_info("alu_base_test",{"\n", m_alu_seq_h.sprint()}, UVM_LOW)

        // Set the generated sequence on the sequencer port of the agent.
        // The driver will pick up a transaction each and driver it.
        m_alu_seq_h.start(m_alu_env_h.m_alu_agent_h.m_sequencer_h);
        #10ns;
        phase.drop_objection(.obj(this));
    endtask: run_phase
endclass: alu_base_test
`endif

12.testbench

`ifndef TB_TOP__SV
`define TB_TOP__SV

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

module top;

reg clk;

// Interface to connect the ALU agent with the DUT
alu_if    m_alu_if_h (clk);

// DUT connections
alu_top DUT (
    .clk_i(clk),
    .rst_i(m_alu_if_h.rst_i),
    .dataA_i(m_alu_if_h.dataA_i),
    .dataB_i(m_alu_if_h.dataB_i),
    .ALUCtrl_i(m_alu_if_h.ALUCtrl_i),
    .ALUResult_o(m_alu_if_h.ALUResult_o),
    .Zero_o(m_alu_if_h.Zero_o)
);

// Clock generator
initial begin
    clk = 0;
    #5ns;
    forever #5ns clk = !clk;
end

// Set the interface to the DUT to be used by UVM TB.
initial begin
    uvm_config_db#(virtual alu_if)::set(null, "*", "alu_if", m_alu_if_h);
    run_test();
end

endmodule: top
`endif

 

你可能感兴趣的:(UVM)