Common package

common package contain commonly used small components.

Random

Common randomization is required for generating space between frame and space between packets. For this purpose two classes have been created (rand_rdy, rand_length). Fist class generates rdy signal. Second class generate number reprezenting length.

There is class inheritance from rand_rdy and rand_length class. This class should be used to generate better randomization. Class rand_rdy and rand_length is common interface.

Folowing example show commonly used for generating space between packet and interfame gaps

//simple sequence
class sequence_simple extends uvm_sequence#(sequence_item);
    `uvm_object_utils(test::sequence_simple);
    uvm_common::rand_rdy    rdy;    //inter frame gabs
    uvm_common::rand_length space;  //space between frames
    function new(string name, uvm_component parent = null);
        super.new(name, parent);
        rdy   = uvm_common::rand_rdy_rand::new();
        space = uvm_common::rand_length_rand::new();
    endfunction
    task body();
        forever begin
            // generate space between frames
            space.randomize();
            for(int unsigned it = 0; it < space.m_value; it++) begin
                send_empty_frame();
            end
            //send transaction
            hl_sequencer.get(hl_transaction);
            for (int unsigned it = 0; it < hl_transaction.data.size(); it++) begin
                //send space inside frames
                rdy.randomize();
                while (rdy.m_value == 0) beign
                    send_empty_frame();
                end
                //send frames
                send_frame(hl_transaction.data[it], it);
            end
        end
    endtask
    ...
endclass

Comparer

These components compare transactions from the DUT with transactions from the model. There are three major classes. All classes have one required parameter and one optional parameter. The first parameter is the type of model’s transactions. The second parameter is the type of DUT transactions.

comparer classes

class

description

uvm_common::comparer_base_ordered#(type MODEL_ITEM, DUT_ITEM = MODEL_ITEM)

The DUT output has to produce transactions in the same order as the model

uvm_common::comparer_base_unordered#(type MODEL_ITEM, DUT_ITEM = MODEL_ITEM)

The DUT output doesn’t have to produce transactions in the same order as the model

uvm_common::comparer_base_tagged#(type MODEL_ITEM, DUT_ITEM = MODEL_ITEM)

The Dut output has to produce transactions in the same order as the model only for each tag. In other words, if the transactions were split per tag into separate streams (one stream for each tag), they must be in order within each stream.

If the type of the model and DUT transactions are the same, then a predefined comparer component can be used. This component has only one parameter - the transaction type.

comparer classes

class

comparer_ordered #(type CLASS_TYPE)

comparer_unordered #(type CLASS_TYPE)

comparer_taged #(type CLASS_TYPE)

All comparers contain a watchdog. You can set up the maximum waiting time for the model and DUT transactions. Maximum waiting time for the model transactions may be necessary if the DUT can create results from partial input.

comparer classes

function

description

dut_tr_timeout_set(time timeout)

How much can the DUT transactions be delayed after the model’s transactions (due to the DUT processing time)

model_tr_timeout_set(time timeout)

How much can the model’s transactions be delayed after the DUT transactions (due to a partial calculation)

The classes contain two analysis ports. The model’s output is connected to the analysis_imp_model port. The DUT’s output is connected to the analysis_imp_dut port. Anything else should be done by classes.

Also, there is the possibility of creating your own comparable algorithm and messages. You can do this by reimplementing pure virtual functions: virtual function int unsigned compare(MODEL_TYPE tr_model, DUT_TYPE tr_dut); and virtual function void message(MODEL_TYPE tr_model, DUT_TYPE tr_dut);. virtual function void message(MODEL_TYPE tr_model, DUT_TYPE tr_dut); The code below provides an example.

// The class extends a specified model and dut type of transactions.
class scoreboard_channel_header #(HDR_WIDTH, META_WIDTH, CHANNELS, PKT_MTU) extends uvm_common::comparer_base_tagged #(packet_header #(META_WIDTH, CHANNELS, PKT_MTU), uvm_logic_vector::sequence_item#(HDR_WIDTH));
    `uvm_component_param_utils(uvm_app_core::scoreboard_channel_header #(HDR_WIDTH, META_WIDTH, CHANNELS, PKT_MTU))

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

    // This method implements a comparison of these two types.
    virtual function int unsigned compare(packet_header #(META_WIDTH, CHANNELS, PKT_MTU) tr_model, uvm_logic_vector::sequence_item#(HDR_WIDTH) tr_dut);
        int unsigned eq = 1;
        logic [META_WIDTH-1:0]meta = 'x;
        logic [$clog2(CHANNELS)-1:0] channel;
        logic [$clog2(PKT_MTU+1)] packet_size;
        logic discard;

        if (META_WIDTH == 0) begin
            {discard, channel, packet_size} = tr_dut.data;
        end else begin
            {discard, channel, meta, packet_size} = tr_dut.data;
        end

        eq &= (discard === tr_model.discard);
        eq &= (channel === tr_model.channel);
        if (META_WIDTH != 0) begin
            eq &= (meta    === tr_model.meta);
        end
        eq &= (packet_size === tr_model.packet_size);

        return eq;
    endfunction

    // This method implements error message printing when an error occurs.
    virtual function string message(packet_header #(META_WIDTH, CHANNELS, PKT_MTU) tr_model, uvm_logic_vector::sequence_item#(HDR_WIDTH) tr_dut);
        string error_msg; //ETH [%0d] header
        logic [META_WIDTH-1:0]meta = 'x;
        logic [$clog2(CHANNELS)-1:0] channel;
        logic [$clog2(PKT_MTU+1)] packet_size;
        logic discard;

        if (META_WIDTH == 0) begin
            {discard, channel, packet_size} = tr_dut.data;
        end else begin
            {discard, channel, meta, packet_size} = tr_dut.data;
        end
        $swrite(error_msg, "\n\t\t          [DUT model]");
        $swrite(error_msg, "%s\n\t\tdiscard [%b %b]", error_msg, discard, tr_model.discard);
        $swrite(error_msg, "%s\n\t\tchannel [%0d %0d]", error_msg, channel, tr_model.channel);
        $swrite(error_msg, "%s\n\t\tmeta    [%h %h]", error_msg, meta, tr_model.meta);
        $swrite(error_msg, "%s\n\t\tpacket_size [%0d %0d]", error_msg, packet_size, tr_model.packet_size);

        return error_msg;
    endfunction
endclass

fifo

This component has been created for connecting models. The problem is when there is an output value of one model changes before entering another model. A common example is when the same model is used in the verification multiple times, and each time, its input(s) are modified differently. Such a scenario is displayed in the example below.

ENTITY_i : entity work.ENTITY_A
port map(
    CLK    => RX_CLK,
    RESET  => RX_RESET,

    DO     => ea_do
);

eb1_di <= ea_do +10;
ENTITY_B1_i : entity work.ENTITY_B
port map(
    CLK    => RX_CLK,
    RESET  => RX_RESET,

    DI     => eb1_di
    DO     => eb1_do
);

eb2_di <= ea_do +20;
ENTITY_B2_i : entity work.ENTITY_B
port map(
    CLK    => RX_CLK,
    RESET  => RX_RESET,

    DI     => eb2_di
    DO     => eb2_do
);

The code below shows the scoreboard for the model connection.

class fifo_en1_input extends uvm_common::fifo#(model_item#(uvm_logic_vector#(32)));
    `uvm_component_utils(fifo_en1_input);

    uvm_analysis_imp_export#(model_item#(uvm_logic_vector#(32)), fifo_en1_input) analysis_export;

    function new(string name, uvm_component parent = null);
        super.new(name, parent);
        analysis_export = new("analysis_expoert", this);
    endfunction

    function void write(model_item#(uvm_logic_vector#(32)) t);
        model_item#(uvm_logic_vector#(32)) item;

        item      = model_item#(uvm_logic_vector#(32))::type_id::create("item", this);
        item.item = uvm_logic_vector#(32)::type_id::create("item", this);

        item.tag   = t.tag;
        item.start = t.start;
        item.item.data = t.item.data + 10;
        this.push_back(item);
    endfunction
endclass

class fifo_en2_input extends uvm_common::fifo#(model_item#(uvm_logic_vector#(32)));
    `uvm_component_utils(fifo_en1_input);

    uvm_analysis_imp_export#(model_item#(uvm_logic_vector#(32)), fifo_en2_input) analysis_export;

    function new(string name, uvm_component parent = null);
        super.new(name, parent);
        analysis_export = new("analysis_expoert", this);
    endfunction

    function void write(model_item#(uvm_logic_vector#(32)) t);
        model_item#(uvm_logic_vector#(32)) item;

        item      = model_item#(uvm_logic_vector#(32))::type_id::create("item", this);
        item.item = uvm_logic_vector#(32)::type_id::create("item", this);

        item.tag   = t.tag;
        item.start = t.start;
        item.item.data = t.item.data + 20;
        this.push_back(item);
    endfunction
endclass

class model_entityb extends uvm_component;
    `uvm_component_utils(model_entityb);

    uvm_common::fifo#(model_item#(uvm_logic_vector#(32))) in;

    function new(string name, uvm_component parent);
        super.new(name, parent);
        in = null;
    endfunction

    ....
endclass

class scoreboard extends uvm_scoreboard;
    `uvm_component_utils(scoreboard);

    model_A m_model_a;
    model_B m_model_b1;
    model_B m_model_b2;

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

    function void build_phase(uvm_phase phase);
        m_model_a  = model_A::type_id::create("m_model_a", this);
        m_model_b1 = model_B::type_id::create("m_model_b1", this);
        m_model_b1.in = fifo_en1_input::type_id::create("in", m_model_b1);
        m_model_b2 = model_B::type_id::create("m_model_b2", this);
        m_model_b2.in = fifo_en2_input::type_id::create("in", m_model_b2);
    endfunction

    function void connect_phase(uvm_phase phase);
        fifo_en1_input mb1_in;
        fifo_en2_input mb2_in;

        $cast(mb1_in, m_model_b1.in);
        m_model_a.do.connect(mb1_in.analysis_export);
        $cast(mb2_in, m_model_b2.in);
        m_model_a.do.connect(mb2_in.analysis_export);
    endfunction
endclass

Also, there is the possibility of merging two inputs into one. When something. The code below shows an example of an input FIFO which merges two inputs into one.

class m_fifo_input extends uvm_common::fifo#(model_item#(uvm_logic_vector#(32)));
    `uvm_component_utils(m_fifo_input);

    uvm_tlm_analysis_fifo#(model_item#(uvm_logic_vector#(16)), m_fifo_input) in_a;
    uvm_tlm_analysis_fifo#(model_item#(uvm_logic_vector#(16)), m_fifo_input) in_b;

    function new(string name, uvm_component parent = null);
        super.new(name, parent);
        in_a = new("in_a", this);
        in_b = new("in_b", this);
    endfunction

    task run_phase(uvm_phase);
        model_item#(uvm_logic_vector#(16)) tr_in_a;
        model_item#(uvm_logic_vector#(16)) tr_in_b;

        model_item#(uvm_logic_vector#(32)) tr_out;

        forever begin
            in_a.get(tr_in_a);
            in_b.get(tr_in_b);

            tr_out      = model_item#(uvm_logic_vector#(32)::type_id::create("tr_out", this);
            tr_out.item = uvm_logic_vector#(32)::type_id::create("tr_out.item", this);

            tr_out.time_array_add(tr_in_a.start);
            tr_out.time_array_add(tr_in_b.start);
            tr_out.tag  = {"M1_", tr_in_b.tag};
            tr_out.item.data = {tr_in_b.item.data, tr_in_a.item.data};
            this.push_back(tr_out);
        end
    endtask
endclass