Monday 7 November 2016

UVM Random Stimulus Generator

In verification using UVM, most of the people create sequences from sequence item and start the same sequence from the testcase. In this blog, we will see a different method to drive transactions without creating any sequence. UVM provides “uvm_random_stimulus” class which generates random transactions and put it on to “blocking_put_port”. So by using this port, we can get the random transaction and drive them on to the interface. “uvm_random_stimulus” class contains “generate_stimulus” method whose arguments are the transaction class and number of transaction to be generated. User can override this method to have his own implementation. It also contains “stop_stimulus_generation” function which stops generation of stimulus.

As we know, the integration of any VIP in the testbench is not completed until a sequence is driven from the driver and monitor samples interface properly. When we integrate any VIP, it may consume time in creating sequences and testcase which could be done later. Here if any error is generated, then user has to find whether there is any problem with component integration or it is related to the sequence or testcase. As this random stimulus generator method doesn’t include any sequences, one can easily stabilize the integration of VIP in the testbench. Error can be generated only because of integration problem (Note that, simulation time error will be printed for erroneous transactions. So constraints should be proper.). So, this method of transaction generation is very useful for the AE(application engineer) and also for a new user of the VIP.

There can be various ways of integrating this random stimulus generator:
One way is, you can create an extra port (put implementation) in the driver and connect it with the stimulus generator put port (“blocking_put_port”). In run phase, get the transaction either from put implementation or seq_item_port based on configuration. In that case, driver contains two ports for getting transactions and it may break the existing functionality.

Another way is, you can override the “get_next_item” method in the sequencer. So without updating the driver code, you can easily integrate the stimulus generator in the testbench. You just need to configure the agent whether to get transaction from sequences or from stimulus generator. Below is the demo example which demonstrates usage of “uvm_random_stimulus” component:
Random Stimulus Generator


// Enum to select transaction generation from sequence or random stimulus generator.
typedef enum 
{
    RANDOM_GENERATOR,
    SEQUENCE_GENERATOR
} txn_generator;

//********************
// Transaction class 
class a_item extends uvm_sequence_item;
    ...  
endclass : a_item

//********************
// Driver class
class a_driver extends uvm_driver#(a_item);
    ... 
    task run_phase(uvm_phase phase);
        super.run_phase(phase);
        forever begin
          seq_item_port.get_next_item(req);
          ...
          seq_item_port.item_done();
        end
    endtask : run_phase
endclass : a_driver

//********************
// Sequencer class
`uvm_blocking_put_imp_decl(_rand)
class a_seqr extends uvm_sequencer#(a_item);
    txn_generator gen=SEQUENCE_GENERATOR;
    uvm_blocking_put_imp_my #(a_item, a_seqr) put_imp;
    ...
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        void'(uvm_config_db#(txn_generator)::get(this,"","gen",gen));
        // Creating implementation port when RANDOM_GENERATOR is selected
        if(gen == RANDOM_GENERATOR)
            put_imp = new("put_imp", this);
    endfunction : build_phase

    // Put implementation
    virtual task put_rand(a_item seq);
        if(seq == null) $display("ERROR: TXN NULL");
        else m_req_fifo.put(seq);
    endtask

    task get_next_item(output a_item t);
        a_item req_item;
  
        if (get_next_item_called == 1)
            uvm_report_error(get_full_name(),
            "Get_next_item called twice without item_done or get in between", UVM_NONE);

        // Calling m_select_sequence method only when transaction from sequences
        // are required. This is the only modification. 
        // Other code remains same as base implementation.
        if ((!sequence_item_requested) && (gen == SEQUENCE_GENERATOR))
            m_select_sequence();

        // Set flag indicating that the item has been requested to ensure that item_done 
        // or get is called between requests
        sequence_item_requested = 1;
        get_next_item_called = 1;
        m_req_fifo.peek(t);
    endtask
endclass : a_seqr

//********************
// Agent class
class a_agent extends uvm_agent;
    a_driver drv;
    a_seqr   seqr;
    a_item   item;
    txn_generator gen=SEQUENCE_GENERATOR;
    uvm_random_stimulus #(a_item) rand_txn_generator;
    int num_rand_txn=20;
    ...
    function void build_phase(uvm_phase phase);
        ...
        // Getting transaction generator
        void'(uvm_config_db#(txn_generator)::get(this,"","gen",gen));
        // Setting transaction generator to sequencer
        uvm_config_db#(txn_generator)::set(this,"seqr","gen",gen);
        // Getting how many transaction should be generated from random stimulus
        // generator.
        uvm_config_db#(int)::get(this,"","num_rand_txn",num_rand_txn);

        `uvm_info("build_phase", $sformatf("Transaction generator is:%0s",gen.name()), UVM_LOW)
        drv  = a_driver::type_id::create("drv", this);
        seqr = a_seqr::type_id::create("seqr", this);

        // Creating random stimulus generator
        if(gen == RANDOM_GENERATOR)
        begin
      rand_txn_generator = uvm_random_stimulus#(a_item)::type_id::create ("rand_txn_generator", this);
          item = a_item::type_id::create("item");
        end
    endfunction : build_phase

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        drv.seq_item_port.connect(seqr.seq_item_export);
        if(gen == RANDOM_GENERATOR)
            rand_txn_generator.blocking_put_port.connect(seqr.put_imp);
    endfunction : connect_phase
  
    task run_phase(uvm_phase phase);
        super.run_phase(phase);
        if(gen == RANDOM_GENERATOR)
        begin
            phase.raise_objection(this);
            rand_txn_generator.generate_stimulus(item, num_rand_txn);
            phase.drop_objection(this);
        end
    endtask : run_phase
endclass : a_agent

//********************
// Environment class
class m_env extends uvm_env;
    a_agent a_ag;
    ...  
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        a_ag = a_agent::type_id::create("a_ag", this);
        ...
    endfunction : build_phase
    ...
endclass :m_env

//********************
// Testcase 
class test extends uvm_test;
    m_env env;
    ...
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        uvm_config_db#(txn_generator)::set(this,"env.a_ag","gen",RANDOM_GENERATOR);
        uvm_config_db#(int)::set(this,"env.a_ag","num_rand_txn",50);
        ...
        env = m_env::type_id::create("env", this);
    endfunction : build_phase
endclass : test

As shown in the example, txn_generator is an enum which is used to select transaction generation from random stimulus generator or from sequences. Here, a_item is a sequence item class and a_driver is a driver class. Sequence item class and driver class implementation doesn't require any extra modification. 

a_seqr is a sequencer class which contains "put_imp" implementation port. When txn_generator is set to RANDOM_GENERATOR, transaction will be put into m_req_fifo from random stimulus generator otherwise it will be put from sequences. Note that, get_next_item method is overridden in the sequencer class. "m_select_sequence" method is called only when txn_generator is set to SEQUENCE_GENERATOR.

In a_agent class, rand_txn_generator is created which is a random stimulus generator component and it's "blocking_put_port" is connected with "put_imp" of sequencer class. In run phase, "generate_stimulus" method is called which generates "num_rand_txn" number of random transactions.

In the testcase, we are just setting "txn_generator" to "RANDOM_GENERATOR" and "num_rand_txn" (number of random transaction to be generated) to 50. So, 50 random transaction will be generated.

This way, without using any sequence user can generate random transactions and drive them on to interface. So, VIP integration task will become much simple.

Note: You may get fatal ("Driver put a response with null sequence_id") if driver is calling "put_response" task for putting response. To avoid it, you can compile with"+define+CDNS_NO_SQR_CHK_SEQ_ID".

4 comments:

  1. This blog reminded of one more method to drive a seq_item directly from the test... thought of sharing it...

    by using execute_item(uvm_sequence_item item) in the sequencer

    So in the test we can create an item and drive it on the sequencer without a sequence...

    #######################

    Class test extends....

    run_task
    a_item item;
    ....create item....
    item.randomize();
    env.sequencer.execute_item(item);

    endtask

    ###################################

    ReplyDelete
  2. I wish the driver had this built-in random generator. And one could config it accordingly.

    ReplyDelete
  3. Hi Munjal,
    Can you provide full working example? I have a question. If there is no sequence, how can I combine the traffic on this interface with other interface traffic either in sequence/parallel? Can we achieve this requirement with this method?

    Thanks,
    Madhusudan

    ReplyDelete
    Replies
    1. Hi Madhusudan,

      "uvm_random_stimulus" class internally randomize the transactions. Number of transaction generation is based in "num_rand_txn" field. As shown in "get_next_item" method of "a_seqr" class, if random stimulus generator is configured then it will generate random transaction otherwise it will take packets from sequence.
      Above is a working example. You just need to add fields in transaction class and need to add constructor method only.

      Please let me know, if still your doubt is not clear.

      Thanks,
      Munjal

      Delete