Showing posts with label uvm. Show all posts
Showing posts with label uvm. Show all posts

Sunday, October 30, 2016

UVM Virtual Sequence Example.

// Base Virtual Sequence Class
class base_vseq extends uvm_sequence #(uvm_sequence_item);
   `uvm_object_utils(base_vseq)
 
   // Target Agent Sequencers
   uvm_sequencer #(ahb_txn) SQR_AHB;
   uvm_sequencer #(axi_txn) SQR_AXI;
 
   // Constructor
   function new (string name = "base_vseq");
      super.new(name);
   endfunction: new
 
endclass: vseq_base

// Virtual Sequence Class
class my_vseq extends base_vseq;
   `uvm_object_utils(my_vseq)
 
   // Constructor
   function new (string name = "my_vseq");
      super.new(name);
   endfunction: new
 
   // Sequence Body Task
   task body();
 
      ahb_seqeunce ahb_seq;
      axi_sequence axi_seq;
 
      ahb_seq = ahb_sequence::type_id::create("ahb_seq");
      axi_seq = axi_sequence::type_id::create("axi_seq");
 
      fork
         abh_seq.start(SQR_AHB);
         axi_seq.start(SQR_AXI);
     join
   endtask
 
endclass: my_vseq

// Base Test Class
class base_test extends uvm_test;
   `uvm_component_utils(base_test);
 
   // Environment Class Instantiation
   top_env Env;
 
   // Constructor
   function new (string name = "base_test", uvm_component parent = null);
      super.new(name, parent);
   endfunction: new
 
   // Build Phase
   function void build_phase (uvm_phase phase);
      Env = top_env::type_id::create("Env");
   endfunction: build_phase
 
   // Method to Connect Sequencer Handles in VSEQ
   function void init_vseq (base_vseq vseq);
      vseq.SQR_AHB = test.env.ahb_agent.SQR_AHB;
      vseq.SQR_AXI = test.env.axi_agent.SQR_AXI;
   endfunction: init_vseq
 
endclass: base_test

// Main Test
class test extends base_test;
   `uvm_component_utils(test)
 
   // Constructor
   function new (string name = "test", uvm_component parent = null);
      super.new(name, parent);
   endfunction: new
 
   // Run Phase
   task run_phase (uvm_phase phase);
      // Create the Virtual Sequence
      my_vseq vseq = my_vseq::type_id::create("vseq");
      phase.raise_objection(this);
      // Virtual Sequence Initialization
      init_vseq(vseq);
      // Start the Virtual Sequence
      vseq.start(null);
      phase.drop_objection(this);
   endtask: run_phase
 
endclass: test

UVM AHB Driver Example.

////////// Sequence Item //////////
class ahb_seq_item extends uvm_sequence_item;
   `uvm_object_utils(ahb_seq_item)
 
   // Master to Slave Data Flow
   rand logic [31:0] HADDR;
   rand logic [31:0] HWDATA;
   rand logic HWRITE;
   ahb_burst_e HBURST;
 
   // Slave to Master Data Flow
   logic [31:0] HRDATA;
   ahb_resp_e HRESP;
 
   // Constructor
   function new (string name);
      super.new(name);
   endfunction: new
 
   // Constraints
   constraint addr_for_32bit {HADDR[1:0] == 0;}
 
endclass: ahb_seq_item


////////// AHB Interface //////////
interface ahb_interface;
 
   // TO both Master & Slave
   logic HCLK;
   logic RESETn;
 
   // Master to Slave
   logic [31:0] HADDR;
   logic [31:0] HWDATA;
   logic HWRITE;
   ahb_burst_e HBURST;
 
   // Slave to Master
   logic [31:0] HRDATA;
   ahb_resp_e HRESP;
   logic HREADY;
 
endinterface: ahb_interface


////////// Pipelined UVM Driver //////////
class ahb_pipelined_driver extends uvm_driver #(ahb_seq_item);
   `uvm_component_utils(ahb_pipelined_driver)
 
   // Virtual Interface
   virtual ahb_interface ahb_if;
 
   // Constructor
   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction: new
 
   // Semaphore Declaration
   semaphore pipeline_lock = new(1);
 
   // Run Phase Task
   task run_phase (uvm_phase phase);
 
      @(posedge ahb_if.HRESETn);
      @(posedge ahb_if.HCLK);
 
      fork
         do_pipelined_transfer;
         do_pipelined_transfer;
      join
 
   endtask: run_phase
 
   // do_pipelined_transfer task
   task automatic do_pipelined_transfer;
 
    ahb_seq_item req;
 
    forever begin
      pipeline_lock.get();
      seq_item_port.get(req);
      accept_tr(req, $time);
      void'(begin_tr(req, "pipelined_driver");
      ahb_if.HADDR <= req.HADDR;
      ahb_if.HWRITE <= req.HWRITE;
      ahb_if.HBURST <= req.HBURST;
      @(posedge ahb_if.HCLK);
      while(!ahb_if.HREADY == 1) begin
         @(posedge ahb_if.HCLK);
      end
      // Command phase ends here
      // Unlock semaphore
      pipeline_lock.put();
      // Data phase starts here
      if (req.HWRITE == 0) begin
         @(posedge ahb_if.HCLK);
         while(ahb_if.HREADY != 1) begin
            @(posedge ahb_if.HCLK);
         end
        req.HRDATA = ahb_if.HRDATA;
        req.HRESP = ahb_if.HRESP;
      end
      else begin
         ahb_if.HWDATA <= req.HWDATA;
         @(posedge ahb_if.HCLK);
         while(ahb_if.HREADY != 1) begin
            @(posedge ahb_if.HCLK);
         end
         req.HRESP = ahb_if.HRESP;
      end
      // Return the Request as Response
      seq_item_port.put(req);
      end_tr(req);
    end
   endtask: do_pipelined_transfer
 
 endclass: ahb_pipelined_driver


////////// Pipelined Sequence ////////// 
 class ahb_pipelined_seq extends uvm_sequence  #(ahb_seq_item);
   `uvm_object_utils(ahb_pipelined_seq)
 
   logic [31:0] addr[10]; // To save addresses
   int count; // To ensure that the seq does'nt complete too early
 
   // Constructor
   function new (string name);
      super.new(name);
   endfunction: new
 
   // Task body()
   task body;
 
      ahb_seq_item req;
      req = ahb_seq_item::type_id::create("req", this);
      use_response_handler(1); // Enable Response Handler
      count = 0;
 
      for(int i=0; i<10; i++) begin
        start_item(req);
        assert(req.randomize() 
            with {MWRITE == 1; HBURST == SINGLE; HADDR 
            inside {[32'h0010_1000:32'h0010_1FFC]};});
        addr[i] = req.HADDR;
        finish_item(req);
      end
 
      foreach (addr[i]) begin
        start_item(req);
        req.HADDR = addr[i];
        req.HWRITE = 0;
        finish_item(req);
      end
 
      // Wait till last seq item is over
      wait(count == 20);
   endtask: body
 
   // This response_handler function is enabled 
   // to keep the sequence response FIFO empty
   function void response_handler(uvm_sequence_item response);
      count++;
   endfunction: response_handler
 
 endclass: ahb_pipelined_seq

UVM PCM Driver Example.

// Interface Declaration
interface adpcm_int;
 
   logic clk;
   logic frame;
   logic [3:0] data;
 
endinterface: adpcm_int


// Transaction item class adpcm_txn
class adpcm_txn extends uvm_sequence_item;
   `uvm_object_util(adpcm_txn)
 
   rand logic[3:0] data;
   rand int delay;
 
   function new (string name);
      super.new(name);
   endfunction: new
 
   constraint del_bet_pac {delay inside {[1:20]};}
 
endclass: adpcm_txn


// Driver class adpcm_drv
class adpcm_drv extends uvm_driver #(adpcm_txn);
   `uvm_component_utils (adpcm_drv)
 
   adpcm_txn req;
 
   virtual adpcm_int adpcm_if;
 
   function new(string name, uvm_components parent)
      super.new(name, parent);
   endfunction: new
 
   task run_phase (uvm_phase phase);
 
      // Default conditions
      adpcm_if.frame <= 0;
      adpcm_if.data <= 0;
      forever begin
         seq_item_port.get_next_item(req);
         repeat(req.delay) begin
            @(posedge adpcm_if.clk);
         end
 
         adpcm_if.frame <= 1;
         for (int i = 0; i < 8; i++) begin
            @(posedge adpcm_if.clk);
            adpcm_if.data <= req.data[3:0];
            req.data = req.data >> 4;
         end
         adpcm_if.frame <= 0;
         seq_item_port.item_done();
      end
   endtask: run_phase
 
endclass: adpcm_drv

                                                                                                                                                                   
// Sequence Class adpcm_seq
class adpcm_seq extends uvm_sequence #(adpcm_txn);
   `uvm_component_utils (adpcm_seq)
 
   adpcm_txn req;
 
   rand int no_of_reps = 10;
 
   function new (string name);
      super.new(name);
   endfunction: new
 
   task body;
      req = adpcm_txn::type_id::create("req", this);
      for (int i = 0; i < no_of_reps; i++) begin
         start_item(req);
         if (!req.randomize()) begin
            `uvm_error("BODY", "req randomization failed")
         end
         finish_item(req);
         `uvm_info("ADPCM_SEQ BODY", 
            $sformatf("Transmitted frame %0d", i), UVM_LOW)
      end
   endtask: body
 
endclass: adpcm_seq

Saturday, October 29, 2016

UVM Coverage.

Basics
  • Put all covergroups in a class or module
  • Use local variables in the class or module
  • Make covergroups sensitive to a variable or explicitly sample a variable
  • Have a coverage test plan
  • Use automatic bins for simple covergroup
  • Code coverage is not functional coverage
    • line coverage
    • block / statement coverage
    • branch coverage
    • path coverage
    • toggle coverage
    • expression coverage
    • FSM coverage
    • transition coverage

<coverpoint_name>  :  coverpoint  <expression>  { bins <bin_name> = { <list of values > } }
class coverage extends uvm_agent;
   `uvm_component_utils (coverage)

   tlm_analysis_fifo #(mem_req) req_fifo;
   mem_req req;
   mem_op op;
   logic [15:0] addr;

   covergroup mem_ops;
      coverpoint memop {
         bins action[] = {read, write};
      }

   coverpoint addr {
         bins zeros  = {0};
         bins others = {[1 : 16'hFFFE]};
         bins ones   = {16'hFFFF};
      }
      edges : cross op, addr;
   endgroup

   covergroup alu_cv;

      all_ops : coverpoint op;
      a_op: coverpoint A {bins Ais0 = {'h00};}
      b_op: coverpoint B {bins Bis0 = {'h00};}

   endgroup

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

   task run();
      mem_data cln;
      mem_req req_tx;
      forever begin : run_loop
         req_fifo.get (req_tx);
         op = req_tx.op;
         addr = req_tx.addr;
         mem_ops.sample();
      end
   endtask : run

endclass


Example: automatic bins
   typedef enum {add, and, xor, mul, rst, nop} op_t;

   covergroup opcov;
      coverpoint op;
   endgroup : opcov

   task sample_req;
      A = req.A;
      B = req.B;
      op = req.op;
      opcov.sample();
   endtask

   covergroup alu_cv2;

      coverpoint op;
      coverpoint A;
      coverpoint B;

      option.auto_bin_max = 4; // default to 64 bins

   endgroup


Example: Basic Bins
   covergroup opcov;
      coverpoint op;
      A00FF : coverpoint A {
         bins zeros = { 0 };
         bins ones  = { 8'hFF };
      }
      B00FF : coverpoint B {
         bins zeros_ones  = { 0, 8'hFF };
      }
   endgroup


Bins with Ranges, Bins with sequences
   typedef enum {add, and, xor, mul, rst, nop} op_t;

   covergroup opcov;
      coverpoint op {
         bins single_cyc = {[add : xor] , rst, nop};
         bins multi_cyc  = {mul};
      }
   endgroup : opcov

   // Automatic bins with ranges
   bins onepervalue [] = {< list of="" values="" >};
   bins n_bins [n] = { < list of="" values="" >};
   bins threebins [3] = {[1:2],[2:6]};
   // same as 
   // bins threebins [3] = {1,2,2,3,4,5,6};

   // Bins with sequences
   // run multi-cycle after reset
   // bins bin_name = ( < value1 > => < value2 >);

   covergroup opcov;
      coverpoint op {
         bins single_cycle = {[add : xor], rst, nop};
         bins multi_cycle  = {mul};
         bins mult_rst = (mul => rst);
         bins rst_mult = (rst => mul);
      }
   endgroup


Bins with multiple value transitions in sequences
 
   // Bins with multiple value transitions
   // bins bin_name = (< value list >  =>  < value list >);
   // 1, 2 => [3:5], 7
   // 1=>3, 2=>3, 1=>4, 2=>4, 1=>5, 2=>5, 1=>7, 2=>7
   bins op_rst[]   = ( [add : nop ] => rst );
   bins rst_mult[] = ( rst => [add : nop]);

   // multi-cycle after single-cycle
   bins singl_mul[]   = ( [add : xor ], nop => mul );

   // single-cycle after multi-cycle
   bins mul_sngl[]   = ( mul => [add : xor ], nop );

   // Run all operations twice in a row 
   // bins <name> = (<value list> [* n]); bins <name> = (<value list> [* n:m]);
   // Ex: Run 3-5 Multiplies in a row
   bins twoops[] = ([add:nop] [*2]);
   bins manymult = (mul [* 3:5]);

   // Nonconsecutive Repetition
   // <value list> [= n:m];     // nonconsecutive operator 
   // <value list> [-> n:m]; // goto operator
   // • Nonconsecutive Operator (=) matches if n:m values occur 
   //        in a list of value regardless of the terminating value.
   // • Goto Operator (->) matches if n:m values occur in a list 
   //        of values and then a terminating value appears.

   bins rstmulrst   = (rst => mul [=  2] => rst);
   // rstmulrst (match): rst => mul => xor => mul => and => rst
   bins rstmulrstim = (rst => mul [-> 2] => rst);
   // rstmulrst and rstmulrstim (match): rst => mul => xor => and => mul => rst


Cross Coverage : Use Cross and Binsof to capture combinations of values
 
   covergroup zeros_ones_ops;

      all_ops : coverpoint op {
         ignore_bins null_ops = {rst, nop};
      }
      a_op: coverpoint A {
         bins zeros  = {'h00};
         bins others = {['h01:'hFE]};
         bins ones   = {'hFF};
      }
      b_op: coverpoint B {
         bins zeros  = {'h00};
         bins others = {['h01:'hFE]};
         bins ones   = {'hFF};
      }

      basic : cross a_op, b_op, all_ops;

      with_a0bin : cross a_op, b_op, all_ops {
         bins a0bin  = binsof (a_op.zeros);
      }

      // bins <bin> = binsof(<somebins>) && binsof(<otherbins>); 
      // bins <bin> = binsof(<somebins>) || binsof(<otherbins>);
      zeros_ones : cross a_op, b_op, all_ops {
         bins AorBzero  = binsof (a_op.zeros) || binsof (b_op.zeros);
         bins AorBones  = binsof (a_op.ones)  || binsof (b_op.ones);
         ignore_bins the_others =
                binsof (a_op.others) && binsof (b_op.others);
      }

      // intersect qualifier
      // bins <bin> = binsof(<somebins> intersect (<value_list>))
      zeros_ones_2 : cross a_op, b_op, all_ops {
         bins add_bin  = binsof (all_ops) intersect {add};
         ignore_bins x = ! binsof (all_ops) intersect {add};
      }

   endgroup



Friday, October 28, 2016

Randomization.


  • $random : return an 32-bit signed random number.
  • $urandom : return an 32-bit unsigned random number.
  • $srandom(seed) : set random seed for $urandm.
  • use run-time switch : +ntb_random_seed=seed_value
  • $urandom_range(min, max)
  • randcase

randcase
   10 : f1();
   20 : f2();
   30 : x = 100;
   50 : randcase ... endcase; // nested
endcase

Thursday, October 27, 2016

UVM Report.


  • Formatting
    • $sformat (str, "%m : a = $2h", a);
    • $timeformat (-9, 2, " ps", 4);
    • timeunit 100ps;
    • timeprecision 10ps;
  • System Calls
    • $display
    • $fdisplay - to write to files.
    • $time, $stime, $realtime,
  • Opening Files
    • integer fha = $fopen("fileaname");
    • integer fhb = $fopen("filebname");
    • integer fhboth = filea | fileb;
    • $fdisplay (fha, "string");
    • $fclose (fhboth);
  • UVM_macros for Reporting
    • $display (`__FILE__);
    • `uvm_info ("message_ID", "This is info message", verbose_level, UVM_INFO);
    • `uvm_warning ("message_ID", "message", Severity);
    • `uvm_error ("message_ID", "message", Severity);
    • `uvm_fatal ("message_ID", "message", Severity);
    • Each reporting method call gets a verbosity number
    • The object has a reporting verbosity number
    • The method acts only if its verbosity number is below the object's verbosity number
    • inst.set_report_verbosity_level_hier (800);
  • UVM Report Control
    • Verbosity level set
    • Reporting methods for actions
      • in end_of_elaboration_phase
      • UVM_DISPLAY
      • UVM_LOG - write to a file
        • Open the file(s) and get the MCDs
        • Set the logging action on the ID's
        • Attach the MCDs to the severities or ID's
          • set_report_default_file() 
          • set_report_id_file ("printer", printer_file);
          • set_report_severity_action
            (UVM_WARNING, UVM_DISPLAY | UVM_LOG);
          • set_report_severity_file (UVM_WARNING, warning_file);
          • dump_report_state();
          • set_report_max_quit_count (10);
      • UVM_COUNT
      • UVM_EXIT
      • UVM_CALL_HOOK - call user defined method
      • UVM_STOP
    • Reporting action controls
      • set_severity_action (severity sv, action a)
      • set_id_action (string id, action a)
      • set_severity_id_action (severity sv, string id, action a)
      • set_severity_action_hier (severity sv, action a)
      • set_id_action_hier (string id, action a)
      • set_severity_id_action_hier (severity sv, string id, action a)
      • Default:
        • UVM_INFO            : UVM_DISPLAY
        • UVM_WARNING  : UVM_DISPLAY
        • UVM_ERROR        : UVM_DISPLAY  |  UVM_COUNT
        • UVM_FATAL         : UVM_DISPLAY  |  UVM_EXIT
    • File ID

UVM Test Analysis.

Analysis Layer

  • Scoreboard / Subscriber / Monitor / Predictor
    • Watch / Observe
    • Recognize transactions
    • Place the transactions to the analysis ports
  • Coverage
  • Reporting
  • Simulation
    • Tests/Transactions -> Drivr -> DUT -> Responder -> TLM models
  • Can connect to multiple objects (unlike uvm_put_port)
  • Analysis Ports, Exports, Imps
  • Port, Export and Imp

Example of Monitor Class
class monitor extends interface_base;
   `uvm_component_utils(monitor)
   uvm_analysis_port #(mem_data) rspa;
   uvm_analysis_port #(mem_req)  reqa;

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

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

   task run_phase(uvm_phase phase);
      mem_req  req = new(), c_req;
      mem_data rsp = new(), c_rsp;

      forever begin : monitor_loop

         @(posedge mif.clk);
         #UNIT_DELAY;
         req.load_data (mif.addr, mif.data, nop);
         rsp.load_data (mif.addr, mif.data);
         if (mif.wr) req.op = write;
         if (mif.rd) req.op = read;
         if (req.op != nop) begin
            $cast(c_req, req.clone());
            reqa.write (c_req);
         end
         if (mif.rd) begin
            $cast (c_rap, rsp.clone());
            rspa.write (c_rsp);
         end

      end : monitor_loop
   endtask
endclass : monitor


Create a Subscriber
  • Instantiate a "uvm_tlm_analysis_fifo"
  • Connect to the analysis_port at the top level
  • Use get() to block and take data out of the analysis fifo



Create a Predictor
  • It gets a request from the analysis_port connected to the monitor.
  • It predicts what response transaction will appear.

class predictor extends uvm_agent;
   `uvm_component_utils (predictor)
   logic [15:0] mem [2**16-1 : 0];

   uvm_tlm_analysis_fifo #(mem_req) reqfifo;
   uvm_put_port #(mem_data) rsp_p; // to connect to comparator
   mem_data rsp = new();

   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);
      req_fifo = new("req_fifl", this);
      rsp_p = new ("rsp_po", this);
   endfunction : build_phase

   task run_phase (uvm_phase phase);
      mem_data cln;
      mem_req req_txn;
      forever begin : run_loop
         req_fifo.get (req_tx);
         case (req_tx.op)
            write : mem[req_tx.addr] = req_tx.data;
            read  : begin : read_op
               rsp.load_data (req_tx.addr, mem[req_tx.addr]);
               $case (cln, rsp.clong());
               rsp_p.put(cln);
            end : read_op
         endcase
      end : run_loop
   endtask : run_phase

endclass : predictor


Create a Comparator and a Printer to Report
  • It gets a response transaction from the monitor.
  • It compares the actual response transaction to the predicted response transaction.

class comparator extends uvm_agent;

   `uvm_component_utils (comparator)

   uvm_tlm_analysis_fifo #(mem_data) actual_f;
   uvm_get_port #(mem_data) predicted_p;
   mem_data actual_rsp, predicted_rsp;

   function new ...

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

   task run_phase (uvm_phase phase);
      forever begin : run_loop
         actual_f.get (actual_rsp);
         predicted_p.get (predicted_rsp);
         if (actual_rsp.comp(predicted_rsp))
            uvm_report_info ("run",
               $psprintf ("passed: %s", 
                          actual_rsp.convert2string()) );
         else
            uvm_report_info ("run",
               $psprintf ("ERROR: expected: %s does not match actual: %s", 
                          predicted_rsp.convert2string(),
                          actual_rsp.convert2string()) );
      end : run_loop
   endtask : run_phase
endclass : comparator

class printer #(type T = mem_data) extends uvm_agent;
   `uvm_component_utils (printer#(T))

   uvm_tlm_analysis_fifo #(T) a_fifo;

   function new ...

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

   task run_phase (uvm_phase phase);
      forever begin
         T data;
         a_fifo.get(data);
         uvm_report_info ("run", data.convert2string());
      end
   endtask : run_phase
endclass : printer


class mem_data extends uvm_transaction;
   `uvm_object_utils (mem_data)
   rand logic [15:0] data;
   rand lobic [15:0] addr;

   virtual function bit comp (uvm_object rhs);
      mem_data RHS;
      $cast (RHS, rhs);
      return ((RHS.addr == addr) && (RHS.data == data));
   endfunction : comp
endclass : mem_data

class mem_req extends mem_data;
...
endclass : mem_req


Top Leven Test Environment
class test_env extends uvm_env;
   `uvm_component_utils (test_env)

   tester tst;
   driver drv;
   uvm_tlm_fifo #(mem_req) tester2driv;

   printer #(mem_req)  req_prt;
   printer #(mem_data) rsp_prt;
   monitor mon;
   predictor pred;
   comparator cmp;
   uvm_tlm_fifo #(mem_data) pred2cmp;

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

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      tst = tester::type_id::create ("tst", this);
      drv = driver::type_id::create ("drv", this);
      tester2drv = new ("tester2drv");

      req_prt = printer#(mem_req)::type_id::create("req_prt", this);
      rsp_prt = printer#(mem_data)::type_id::create("rsp_prt", this);

      mon = monitor::type_id::create ("mon", this);
      pred = predictor::type_id::create ("pred", this);
      cmp = comparator;:type_id::create ("cmp", this);
      pred2cmp = new("pred2cmp"< this);
   endfunction : build_phase

   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase(phase);
      tst.tb_port.connect (tester2drv.put_export);
      drv.req_f.connect (tester2drv.get_export);
      cmp.predicted_p.connect (pred2cmp.get_export);
      pred.rsp_p.connect(pred2cmp.put_export);
      mon.req_a.connect (pred.req_fifo.analysis_export);
      mon.rsp_a.connect (cmp.req_fifo.analysis_export);
      mon.req_a.connect (req_prt.a_fifo.analysis_export);
      mon.rsp_a.connect (rsp_prt.a_fifo.analysis_export);

   endfunction : connect_phase

endclass : test_env

class bucket #(type T = mem_data) extends printer #(T);

   typedef bit_bucket#(T) thistype;
   `uvm_component_param_utils (thistype)

   function new ...

   task run_phase (uvm_phase phase);
      forever begin
         T data;
         a_fifo.get(data);
      end
   endtask : run_phase

endclass : bucket

class qtest extends uvm_test;
   `uvm_component_utils (qtest)

   test_env t_env;

   function new ...

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      printer#(mem_data)::type_id::set_type_override
         (bucket#(mem_data)::get_type());
      printer#(mem_req)::type_id::set_type_override
         (bucket#(mem_data)::get_type());
      t_env = test_env::type_id::create ("t_env", this);
   endfunction : build_phase

endclass : qtest



Wednesday, October 26, 2016

UVM Configuration DB Gotchas.


  • Missing super.build_phase()
  • Missing field macro
  • UVM_READONLY on field
  • Missing get() after set() changes the entry
  • uvm_config_object::set() writes a null object
  • Wrong or mismatched types
  • Missing $cast in explicit get()
  • Wrong string in field names, wrong context
  • Not a good practice to use wildcard in ::set() - creating too much visibility

 References:

UVM Configuration DB.

Concepts
  • Share information between
    • Test
      • Top environment
        • sub environment
          • agents
            • sequencers
            • monitors
            • drivers
  • Need a consistent way of storing shared data 
  • Need simple, flexible access from anywhere
  • Need automatic access (done by uvm_component::build_phase())
  • Compare to traditional
    • enclosing pushes
    • ex. env.m_this_config = m_cfg
How to Use
  • Share the information of Physical IFs (between DUT and uvm_test)
    • Put into configuration db at top test module and get it in
      uvm_env.uvm_agent
      uvm_env.uvm_agent.uvm_driver
      uvm_env.uvm_agent.uvm_sequencer
      uvm_env.uvm_agent.uvm_monitor
  • Share DUT specific information

Syntax
//
// UVM 1.1
set_config_int(...) => uvm_config_db#(uvm_bitstream_t)::set(cntxt,...)
set_config_string(...) => uvm_config_db#(string)::set(cntxt,...)
set_config_object(...) => uvm_config_db#(uvm_object)::set(cntxt,...)
//
// UVM 1.2
// type T=int
// class uvm_config_db#(type T=int) extends uvm_resource_db#(T)
//
//                   context, inst,  key,   value   
uvm_config_db#(T)::set(cntxt,"inst","field",value); // modify central entries
uvm_config_db#(T)::get(cntxt,"inst","field",value); // update local variabbles
// There is a uvm11-to-uvm12.pl conversion script
uvm_config_db#(T)::exists(cntxt,"inst","field");
uvm_config_db#(T)::wait_modified(cntxt,"inst","field");

// For objects use this
set_config_object::set(this,"env.agent1","config", m_config);
// For enums use this
set_config_int::set(this,"env.agent1","bus_sz", SZ_128 );

// cntxt must be of type uvm_component, null or uvm_root::get()
// m_config: recommend to use uvm_object and $cast 
// Examples:
uvm_config_db#(T)::set(uvm_root::get(),”test1.env.agent1”, ”config”, m_config);
uvm_config_db#(T)::set(this,”env.agent*”,”config”, m_config);
uvm_config_db#(T)::set(null,”*”,”global_cfg”, m_global_cfg);

// to fetch the entry
if (!uvm_config_db#(uvm_object)::get(this, "","config",m_config)) begin
   `uvm_fatal(...) 
end
// check if it exists
if(!uvm_config_db#(int)::exists(this,"","low_power_mode")) begin 
   // do something interesting
end

// To automate field configuration
// Use uvm_config_object typedef
// Use uvm_object and $cast to concrete type for get()
function void uvm_component::build_phase(...); 
   // find fields, do get(), $cast
   apply_config_settings(...); 
endfunction

// Gotchas: 1. Don't miss field-macro
//         2. Make sure it's not UVM_READONL
/          3. remember to use super.build

class my_comp extends uvm_component;
   `uvm_component_utils_begin(my_comp)
      `uvm_field_int(my_field,UVM_DEFAULT) 
      `uvm_field_object(my_special,(UVM_DEFAULT|UVM_READONLY))
      `uvm_field_object(my_config,UVM_DEFAULT)  

      function void build_phase(...);
         super.build_phase(...);
         ...
      endfunction

// Using uvm_object and $cast in set() and get()
my_config m_config;
   ...
uvm_config_object::set(..., m_config);

uvm_object tmp;
uvm_config_object::get(..., tmp); 
$cast(m_config, tmp); // back to original type

// Using enums (can be int, uvm_bitstream_t or uvm_integral_t
my_enum_t m_bus_sz;
uvm_config_int::set(... , "m_bus_sz", SZ16);

int tmp;
uvm_config_int::get(... ,"m_bus_sz", tmp)
m_bus_sz = my_enum_t'(tmp); // back to original type

// Recommend use the factory 
m_srio_config = uvc_srio_config::type_id::create("m_srio_config");


Hierarchy of Configuration Objects
class top_env_config extends uvm_object; 
   env1_config m_env1_config; 
   env2_config m_env2_config;
   ...
endclass

class env1_config extends uvm_object;
   int some_var;
   int some_other_var;
   agent1_config m_agent1_config;
   agent2_config m_agent2_config;
   ... 
endclass

class top_env extends uvm_env;
   env1 m_env1;
   env2 m_env2;
   top_env_config m_config;
   ...
   'uvm_component_utils_begin(top_env)
      'uvm_field_object(m_config, UVM_DEFAULT)
   'uvm_component_utils_end

   function build_phase();
      super.build_phase();
      set_config_object(this, "env1","m_config",
                       m_config.m_env1_config);
      set_config_object(this, "env2","m_config",
                       m_config.m_env2_config);
      m_env1 = env1::type_id::create("m_env1");
      m_env2 = env2::type_id::create("m_env2");
   endfunction
endclass

// Next Level Down
class env1 extends uvm_env; 
   env1_config m_config;
   ...
   'uvm_component_utils_begin(top_env)
      'uvm_field_object(m_config, UVM_DEFAULT)
   'uvm_component_utils_end

   function void build_phase();
      super.build_phase();
      set_config_object(this, "m_agent1","m_config",
                m_config.m_agent1_config); 
      set_config_object(this, "m_agent2","m_config",
                m_config.m_agent2_config); 
      m_agent1 = env1::type_id::create("m_agent1"); 
      m_agent2 = env2::type_id::create("m_agent2");
   endfunction
endclass


Turn on the debugging in simulation

% sim_cmd +UVM_TESTNAME=my_test +UVM_CONFIG_DB_TRACE


UVM Scoreboard Methodology.

Principles and Concepts
  • Verify transaction functional correctness with the following approaches
    • constrained-randomization
    • coverage driven
    • transaction-level compartment
    • Use sequences
    • Use scoreboard
    • Use configuration-db
  • Use SystemVerilog to provide aggregate types for transaction storage and search
    • Dynamic Arrays, Associative Arrays, Queues, Classes
  • UVM analysis ports is easier to use than callbacks
  • UVM_transaction provides compare() / do_compare() methods
  • UVM factor for extension, replacement and reuse.

Plan of Attack
  • Analysis_port
    • port, export and imp classes used for transaction analysis
    • uvm_analysis_port - broadcasts a value to all subscribers implementing a uvm_analysis_imp
    • uvm_analysis_imp - receives all transactions broadcasted by a uvm_analysis_port.
  • Transaction Predictor
    • Required when the compared transaction is not the same format as input (ex. protocol bridges)
    • Design transforms data (ex. encryption, filter, encoder)
    • To Re-Use
      • encapsulation : a separate class
      • uvm_analysis_port / imp to connect
      • UVM factory to extend, replace existing objects
      • Replace complex predictors with several simpler scoreboards
  • Storage (for read scoreboard and for write scoreboard)
    • FIFO : queues
    • OOO : dynamic arrays, queues
    • MEM : associative array (indexed by address)
    • Associative array of queues
  • TLM Model
    • Used when you have multiple different transaction paths/routes
    • Used when behavior depends on the address or opcodes or other attributes of transactions
  • Using Factory
  • Interconnect
  • Automate by Randomization
  • Checks and Coverage
  • Assertions
  • Pool



Transactions
  • Transaction content
    • Data, address, Attributes, Opcode, Response, 
    • Data types:
      • memory -> scalar, bytes, integers, 
    • Data path:
      • Scalar
      • Transaction Item Class
  • Transaction ordering
    • FIFO
    • OOO
    • Precedence relationship
  • Test Data/Transactions -> Item of Sequences -> Sequences -> Sequencer -> Driver -> VIF -> DUT
  • DUT -> monitor -> Analysys_ports -> Scoreboard




Monday, October 24, 2016

UVM / System Verilog - Threads and Synchronization.


  • In SystemVerilog, when you instantiate static modules, you create multiple threads running the procedures within each instances.
  • Synchronization
    • Mailbox
    • Streams
    • Semaphore
    • Use Ports, Exports and TLM_FIFOs
      • uvm_put_port # (int) p1;
        p1 = new("put_port", this);
      • uvm_get_port #(int) p2;
      • uvm_tlm_fifl #(int) p3;
      • m1.p1.connect (p3.put_export);
        m2.p2.connect (p3.get_export);
      • // in f1.svh, for class c1
           uvm_put_port # (int) p1;
           p1 = new("put_port", this);
        // in f2.svh, for class c2
           uvm_get_port #(int) p2;
           p2 = new("p2", this);
        // in f3.svh, which contains the 
        //     test env for c1 and c2
           uvm_tlm_fifo #(int) p3;
           p3 = new("p3", this);
           c1.p1.connect (p3.put_export);
           c2.p2.connect (p3.get_export0;
        
        // c1.p1 -> (put_export of env)
        //               ->(p3)->
        //          (get_export of env) -> c2.p2
        
           
        endtask : run
        
    • Put shared variable in a package, together with all the classes that share that variable (using `include);
    • virtual task run();
         integer i;
         for (i = 1; i < cnt; i++) begin : loop
            uvm_report_info ("run", $psprintf(...));
            my_pkg::shared = i; // used by other classes 
                                // for synchronization
            // i = my_pkg::shared; // to read from the shared
         end : loop
      endtask : run
      
      
    • Blocking Methods vs Non-Blocking Methods
      • blocking method
        • myport.put(<data>);
        • myport.get(<data>);
      • non-blocking method (returns 0 if failed)
        • myport.try_put();
        • myport.try_get();

Example
class c1 extends uvm_agent;
   uvm_put_port #(int)p1;
   `uvm_component_utils(c1)

   function new...

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

   virtual task run_phase (uvm_phase phase);
      phase.raise_objection(this);
      for (int i = 0; i < cnt; i++) begin
         p1.put(i);
         uvm_report_info("run_phase",...);
      end : loop
      phase.drop_objection(this);
   endtask : run_phase
endclass : c1

class c2 extends uvm_agent;
   uvm_put_port #(int)p2;
   `uvm_component_utils(c2)

   function new...

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

   virtual task run_phase (uvm_phase phase);
      int i;
      forever begin : loop
         p2.get(i);
         uvm_report_info("run_phase",...);
      end : loop
   endtask : run_phase
endclass : c2

class c3 extends uvm_agent;

   `uvm_component_utils(c3)

   c1 p1;
   c2 p2;
   uvm_tlm_fifo #(int) p3;

   function new...

   virtual function void build_phase (uvm_phase phase);
      super.build_phase(phase);
      p1 = c1::type_id::create("p1", this);
      p2 = c2::type_id::create("p2", this);
      p3 = new("p3", this);
   endfunction : build_phase

   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase(phase);
      c1.p1.connect(p3.put_export);
      c2.p2.connect(p3.get_export);
   endfunction : connect_phase

   virtual task run_phase (uvm_phase phase);
   endtask : run_phase

endclass : c3


Example : Blocking Communication vs Non-blocking Communication
// blocking
// @(posedge clk); // this could be missed 
//                   and block the following
// my_port.get(data);
// bus <= data; 

// non-blocking
// @(posedge clk)
// my_port.try_get(data); // won't suspend
// bus <= data;
//

class c1 extends uvm_agent;

   `uvm_component_utils(c1)
   uvm_put_port #(int)p1;

   function new...

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

   virtual task run_phase (uvm_phase phase);
      phase.raise_objection(this);
      for (int i = 0; i < cnt; i++) begin
         // p1.put(i);
         // uvm_report_info("run_phase",...);
         #UNIT_DELAY; // to delay this thread so the following
                      // is always slower than p2 and 
                      // should never fail
         assert (p1.try_put(i)) else
            uvm_report_info("run_phase: p1 not connected");
         uvm_report_info("run_phase", $psprintf("put: %0d",i));
      end : loop
      phase.drop_objection(this);
   endtask : run_phase
endclass : c1

class c2 extends uvm_agent;

   `uvm_component_utils(c2)
   uvm_put_port #(int)p2;

   function new...

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

   virtual task run_phase (uvm_phase phase);
      int i;
      forever begin : loop
         // p2.get(i);
         // uvm_report_info("run_phase",...);
         if (p2.try_get(i))
            uvm_report_info ("run",
               $psprintf ("got: %0d",i));
         else
            uvm_report_info ("run","got nothing");
      end : loop
   endtask : run_phase
endclass : c2



UVM - Scoreboard, Checking and Reporting.


  • Scoreboard
  • Reporting printer
  • Using create

Scoreboard

  • Scoreboard could hold the entire self-checking structure including the transfer function or reference model, the expected data storage mechanism and the output comparison function . It could also be limited to the data structure used to hold the expected data for ease of comparison against the monitored output values.
  • Stimulus Generator or Sequencer
    • DUT -> Response Monitor
    • TLM or transfer function
  • Output from the DUT responses are compared with expected result from transfer model

class scoreboard extends uvm_agent;
   `uvm_component_utils(scoreboard)

   virtual interface mem_if vif;
   logic [15:0] exp [2**16-1:0];

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

   task run_phase (uvm_phase phase);
      forever begin
         @ (vif.cb)
         if (vif.rd) begin
            #`UNIT_DELAY
            assert (vif.data === exp[vif.addr]) else
               uvm_report_error ("run",
                  $psprintf("expected %0h  actual: %0h",
                      exp[vif.addr], vif.data));
         end
         if (vif.wr) begin
            exp[vif.addr] = vif.data;
         end
      end
   endtask : run_phase 


Reporting
class reporter extends uvm_agent;

   `uvm_component_utils (reporter)
   virtual interface mem_if vif;

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

   task run_phase (uvm_phase phase);
      forever begin
         @(vif.cb);
         uvm_report_info ("run",
            $psprintf ("addr: %1h  data:%4h  rd:%1b  wr: %1b",
               vif.addr, vif.data, vif.rd, vif.wr));
      end
   endtask : run_phase
endclass : reporter

class bucket extends reporter;

   `uvm_component_util(bucket)

   function new ...

   task run_phase (uvm_phase phase); // override to do nothing
   endtask : run_phase

endclass : bucket



Create Tests
// In the test class file
class test1 extends uvm_test;

   `uvm component_utils(test1)

   test_env tenv;

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

   virtual function void build_phase (uvm_phase phase);
      tenv = test_env::type_id::create("tenv", this);
   endfunction : build_phase

endclass

class test2 extends uvm_test;

   `uvm component_utils(test2)

   test_env tenv;

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

   virtual function void build_phase (uvm_phase phase);
      // When you call the overriding run_phase in bucket,
      // the test will run without printing line by line data in
      // in default reporter.run_phase
      reporter::type_id::set_type_override(bucket::get_type());
      tenv = test_env::type_id::create("tenv", this);
   endfunction : build_phase

endclass : test2

class test_env extends uvm_env;

   `uvm_component_utils (test_env)

   driver drv;
   scoreboard sb;
   reporter rpt;

   function new ...

   virtual function void build_phase (uvm_phase phase);
      drv = driver::type_id::create("drv", this);
      sb  = scoreboard::type_id::create("sb", this);
      rpt = reporter::type_id::create("rpt", this);
   endfunction : build_phase

endclass



UVM Connections.

Using Interface
  • Virtual interface
  • Static interface

Example 1
`include "uvm_macros.svh"

package my_pkg;
   import uvm_pkg::*;
   int shared_int;
   int shared_count;

   virtual interface mem_if global_vmi;
   `include "uvm_macros.svh"
   `include "test_1.svh"
   `include "test_2.svh"

endpackage: my_pkg

import uvm_pkg::*;
import my_pkg::*;

module top;
   mem_if mi();
   sram dut (mi.mem_modport);

   initial begin
      string test_name;
      my_pkg::global_vmi = mi;
     run_test();
   end

endmodule: top

// typedef struct packed
typedef struct {
   bit     sign;
   bit[24:0] mantissa;
   bit[ 5:0] exponent;
} ieee_sp_float;

union packed {
   bit [7:0] data[1500];
   struct packed {
      bit [7:0] dsap;
      bit [7:0] ssap;
      bit [7:0] control;
      bit [7:0] data[1497];
   } label;
} payload;

class test_1 extends uvm_test;
   `uvm_component_utils(test_1)

   ieee_sp_float v1, v2;
   v1 = {1, 24'h800, 6'h0};
   v1 = abs(v1);
   virtual interface mem_if mi;

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

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      mi = my_pkg::global_vmi;
   endfunction : build_phase

   virtual task run_phase(uvm_phase phase);

      int cnt=5;
      logic [3:0] sub_addr;
      rand logic [11:0] upper_addr;
      sub_addr = $urandom($random);
      phase.raise_objection(this);
      mi.wr = 1'b1;
      mi.rd = ~mi.wr;
      mi.wr_data = $urandom($random);
      `uvm_info("TESTER", 
         $psprintf("addr: %2h  data: %2h  rd: %1b  wr: %1b", 
         mi.addr, mi.data, mi.rd, mi.wr), UVM_INFO);

      super.run();
      mi.addr = {upper_addr, sub_addr};

      repeat (cnt) begin
         @(posedge mi.clk);
         ...
      end
         @(posedge mi.clk);

         phase.drop_objection(this);

   endtask // run_phase


Example 2
import my_pkg::*;

// Virtual interface in driver
class my_driver extends uvm_driver #(my_transaction); 

   `uvm_component_utils(my_driver)

   virtual dut_if dut_vi;

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

   // function void build;
   virtual function void build_phase (uvm_phase phase);
      super.build();
      dut_vi = global_dutvi;
   endfunction : build_phase

   // task run;
   task run_phase (uvm_phase phase);
      repeat(4)
      begin
         phase.raise_objection(this);
         my_transaction tx;
         @(posedge dut_vi.clock);

         // Driver consumes the transactions generated by
         // my_transaction and wiggles the pins on the DUT
         seq_item_port.get(tx);

         // Pin Wiggling
         dut_vi.cmd  = tx.cmd;
         dut_vi.addr = tx.addr;
         dut_vi.data = tx.data;

      end
      @(posedge dut_vi.clock) top.stop_request();
      phase.drop_objection(this);

   endtask: run

endclass : my_driver


Example 3, connecting sequencer to driver
class my_sequencer extends uvm_sequencer;
   ...
   uvm_put_port #(int) port1;
   port1 = new ("port1", this);

endclass

class my_driver extends uvm_driver;
   ...
   uvm_get_port #(int) port2;
   port2 = new ("port2", this);

endclass

class my_agent extends uvm_agent; 
   `uvm_component_utils(my_agent) // register for type_id

   my_sequencer my_sequencer_h;
   my_driver my_driver_h;

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

   function void build;
      super.build(); 
      my_sequencer_h = 
         my_sequencer::type_id::create("my_sequencer_h", this);
                                     // instance name,   parent
      my_driver_h =
         my_driver::type_id::create  ("my_driver_h" , this);

   endfunction: build

   // Lower level connection in another function is good practice
   // seq_item_port is the handler of the ports

   function void connect; 
      my_driver_h.seq_item_port.connect(
         my_sequencer_h.seq_item_export ); 
   endfunction: connect

endclass : my_agent


Example 4, connecting through top module
`timescale 1ns/1ns

module top;

   mem_if mi();
   sram dut (mi.mem_modport);
   tester tst (mi.test_modport);

endmodule // top

interface mem_if;
   bit clk, rd, wr;
   logic [15:0] wr_data;
   wire  [15:0] data;
   logic [15:0] addr;

   modport mem_modport (
      inout data,
      input addr,
      input clk,
      input rd,
      input wr
   );

   modport test_modport (
      input clk,
      output wr_data,
      output addr,
      output rd,
      output wr
   );

   clocking cb @ (negedge clk);
      output data <= ...;
      output addr <= ...;
   endclocking: cb

   initial begin
      @(cb) ; wr_data <= ...;
      @(cb) ; wr <= $urandom;
   end

   assign rd = ~wr;
   assign data = (wr) ? wr_data : 16'hzzzz;

   initial begin
      clk = 0;
      $monitor ();
   end

   always #10 clk = ~clk;

endinterface // mem_if


Example 5, connecting in tester
module top;

   mem_if mi();
   sram dut (mi.mem_modport);
   tester tst;

   initial begin
      tst = new(mi);
      fork
         tst.run;
      join_none
   end

endmodule // top

class tester; // driver

   logic [3:0] sub_addr;
   virtual interface mem_if tmi;

   function new (virtual interface mem_if vmi);
      tmi = vmi;
   endfunction // new

   task run;
      // generate transaction data
      tmi.wr = 1'b1;

      @ (posedge tmi.clk); 
      repeat (100) begin
         @ (posedge tmi.clk); 
         tmi.wr = $random;
         tmi.rd = ~tmi.wr;
         tmi.addr = $random;
         tmi.wr_data = $urandom($random);
      end
      $stop;
   endtask // run

endclass // tester


Example 6, use wrapper_if
module top;

import uvm_pkg::*;
import my_pkg::*;

   mem_if mi();
   sram dut (mi.mem_modport);
   // tester tst; // replaced by if_wrapper

   initial begin: blk
      dut_if_wrapper if_wrapper = new ("if_wrapper", dut_if_inst1);
                   // path  field_name        value      0: don't clone
      set_config_object("*", "dut_if_wrapper", if_wrapper, 0);

      run_test ("my_test");
   end

endmodule : top

class dut_if_wrapper extends uvm_object;

   virtual dut_if dut_vi;
   function new (string s, virtual dut_if if_arg); 
      super.new(s);
      dut_vi = if_arg;
   endfunction : new

   // you can have task run; here

endclass : dut_if_wrapper

`include "uvm_macros.svh"

package my_pkg;
import uvm_pkg::*;

// Fixed test environment
class my_env extends uvm_env;
    `uvm_component_utils (my_env)

    virtual dut_if dut_virtual_if_inst;

    // constructor
    function new (string s, uvm_component inst_parent);
        super.new (s, inst_parent);
    endfunction : new

    function void build;
        super.build ();
        begin
            uvm_object obj;
            dut_if_wrapper if_wrapper; 
            get_config_object("dut_if_wrapper", obj, 0);
            assert( $cast(if_wrapper, obj) );
            dut_virtual_if_inst = if_wrapper.dut_vi;
        end
    endfunction : build

    task run;
        #10 dut_virtual_if_inst.data = 0;
        #10 dut_virtual_if_inst.data = 1;
        #10 stop_request();
    endtask : run

endclass : my_env


Example 7, use uvm_get_port and uvm_put_port in tlm_fifo / tlm_analysis_fifo
class producer extends uvm_agent;
   uvm_put_port #(int) phone1;
   ...

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

   virtual task run_phase (uvm_phase phase);
      phase.raise_objection(this);
      for (int i = 0; i < count; i++) begin : loop
         phone1.put(i);
         uvm_report_into ("run_phase", $psprintf("...");
      end : loop
      phase.drop_objection(this);
   endtask : run_phase

endclass : producer

class consumer extends uvm_agent;
   uvm_get_port #(int) phone2;
   ...

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

   virtual task run_phase (uvm_phase phase);
      phase.raise_objection(this);
      for (int i = 0; i < count; i++) begin : loop
         phone1.put(i);
         uvm_report_into ("run_phase", $psprintf("...");
      end : loop
      phase.drop_objection(this);
   endtask : run_phase

endclass : producer

class test_env extends uvm_env;

   producer p;
   consumer c;
   uvm_tlm_fifo #(int) tlmff;

   function new () ...

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      p = producer::type_id::create ("p", this);
      p = producer::type_id::create ("p", this);
      tlmff = new ("tlmff", this);
   endfunction : build_phase

   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase (phase);
      p.phone1.connect (tlmff.put_export);
      c.phone2.connect (tlmff.get_export);
   endfunction : connect_phase


Example 8, using uvm_config_db
module top;
   ...
   dut_if dut_if1 ();
   initial
   begin: blk
      //             type of value        prefix   path
      uvm_config_db #(virtual dut_if)::set(null, “uvm_test_top”, 
                  "dut_vi", dut_if1);
      //          field name, value
      run_test("my_test");
   end
endmodule: top

class my_test extends uvm_test;
   ...
   my_dut_config dut_config_0; 
   ...

   function void build_phase(uvm_phase phase);
      dut_config_0 = new();
      //             type of value    prefix   path
      if(!uvm_config_db #(dut_if)::get( this, “”, 
                        “dut_vi”, dut_config_0.dut_vi))
             `uvm_fatal(“MY_TEST”, “No DUT_IF”);

      // other DUT configuration settings
      uvm_config_db#(my_dut_config)::set(this, “*”, “dut_config”,
                                       dut_config_0);     
    endfunction
endclass

class my_driver extends uvm_driver;
   `uvm_component_utils(my_driver)
   virtual dut_if dut_vi;

   function new(string name, uvm_component parent); ...

   function void build_phase(uvm_phase phase); ...

   task run_phase(uvm_phase phase);
      phase.raise_objection(this);
      #10 dut_vi.data = 0;
      #10 dut_vi.data = 1;
      #10 phase.drop_objection(this); 
   endtask: run_phase

endclass


Friday, October 21, 2016

UVM 101.

Packages
  • Provide a common name space •
  • Can be imported • 
  • Can contain common objects •
// You can have the followings in a package
// • Common typedefs 
// • Common class definitions
// • Shared instances or variables

import uvm_pkg::*;

`include "defines.svh"
`include "macro_defines.svh"
`include "test_plugin.svh"

module top;
        initial begin
                #10ns;
                uvm_top.uvm_report_info ($psprintf("%m"),
                        "This is an info message");
        end
endmodule


Macros
`define info(msg)    uvm_top.uvm_report_info($psprintf("%m"), msg);
`define warning(msg) uvm_top.uvm_report_warning($psprintf("%m"), msg);
`define error(msg)   uvm_top.uvm_report_error($psprintf("%m"), msg);
`define fatal(msg)   uvm_top.uvm_report_fatal($psprintf("%m"), msg);

import uvm_pkg::*;

module top;
    initial begin
        `info ("My info message goes here");
        `warning ("My warning message goes here");
        `error ("My error message goes here");
        `fatal ("My fatal message goes here");
    end
endmodule

`ifdef FPGA

module sythesizable_block;
endmodule // monitor

`else

module beh_model;
endmodule // beh_model;

`endif // !`ifdef FPGA

// Define macro when you compile in dofile
% if (file exists work) {delete -all}
% vlib work
% vlog top.sv +define+FPGA
% vsim -novopt top
% run -all

Classes
class read_op extends memory_op;
    constraint read_only_c {mem_op == read;};
endclass // read_op

// • Put each class in individual .svh file
// • Put all .svh files in a package "tb_pkg" using `include
// • Put all files in a file list "vfiles.f" to compile 
// • In the top test bench file, import "uvm_pkg" and this "tb_pkg"


UVM package
  • uvm_report_object
    • uvm_cmdline_processor
    • uvm_component
      • uvm_agent
      • uvm_driver #()
      • uvm_env
      • uvm_monitor
      • uvm_port_component_base
      • uvm_root
      • uvm_scoreboard
      • uvm_sequencer_base
      • uvm_test
      • uvm_tlm_fifo_base #()
  • UVM phases used in uvm_test
    • function void build_phase()
    • function void connect_phase()
    • function void end_of_elaboration_phase()
    • function void start_of_simulation_phase()
    • function void run_phase()
    • function void extract_phase()
    • function void check_phase()
    • function void report_phase()
  • Use "+UVM_TESTNAME=test1" to run different tests
    • The sub classes extended rom uvm_test with UVM_TESTNAME plusarg will start the run_test() that is defined in uvm_pkg and the run_test() uses UVM factory pattern to create a test object.

class test1 extends uvm_test;
   `uvm_component_util(test1)

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

    task run_phase(uvm_phase phase);
        phase.raise_objection(this); // tell UVM this thread started
        super.run();
        `uvm_info("test1: ", "Starting...", UVM_INFO);
        phase.drop_objection(this);  // tell UVM this thread finished
    endtask
endclass : test1

// in top module
module top;

    initial run_test(); // this will create a test object 
                        // and executes its run_phase() 
endmodule : top


UVM Structure
  • Top
    • uvm_test or program in SystemVerilog
      • task run();
      • uvm_env
        • uvm_agent
          • driver
            • run
          • monitor
            • $monitoron / $monitoroff
            • sync_reset
            • semaphore
              • semaphore sem = new(1);
              • sem.get(1) ... sem.put(1)
              • `uvm_raise_objection(this)
                / `uvm_drop_objection(this)
            • disable <block_name>;
          • sequencer
            • sequence
              • body
              • start
            • transaction
              • build
              • run
        • uvm_scoreboard or ovm_subscriber
        • uvm_agent for coverage
    • interface
    • DUT

UVM Randomization and Constraints
typedef logic [15:0] addr_type;
typedef logic [7:0] data_type;
typedef enum {read, write, nop} op_type;

class my_op;
   rand addr_type addr;
   rand data_type data;
   rand op_type cmd;
   ...
endclass : my_op

class read_op extends my_op;
   constraint read_only_cmd {my_op == read;};
endclass // read_op

module top;
   my_op op;
   read_op rdop;
   addr_type addr2;

   initial begin
      op = new();
      addr2 = $random;
      repeat (2) begin

         assert(op.randomize());
         // with constraint
         assert(op.randomize() with {my_op == read;});

         assert(rdop.randomize()); // this will be limited to read only

      end
   end
endmodule // top



Tuesday, October 11, 2016

UVM/OVM Questions.


If you are preparing for a Verification job interview or if you are trying to master this methodology for your next project, here are some of the questions that you can use to test your skills and measure yourself:

  1.  What are some of the benefits of UVM methodology? 
  2. What are some of the drawbacks of UVM methodology? 
  3. Explain the concept of Transaction Level Modelling? 
  4. What is the difference between an uvm_object and uvm_component class? 
  5. What are TLM ports and TLM Fifos? 
  6. What is an analysis port and analysis fifo and where are they used? 
  7. Explain the protocol handshake between a sequencer and driver ? 
  8. What is the difference between a sequence and sequence item? 
  9. Is it possible to collect responses from DUT back to a sequence and if so how? 
  10. What is the difference between SEQ_ARB_RANDOM and SEQ_ARB_STRICT_RANDOM arbitration mechanism on sequencer? 
  11. What is the difference between grab() and lock() on sequencer? 
  12. What is the difference between a pipelined and non-pipelined driver? 
  13. What is the difference between early randomization and late randomization of sequences? 
  14. Write a sample sequence code that generates a stream of ethernet packets? 
  15. How can you specify weightage for a sequence when started on a sequencer? 
  16. What is the difference between a monitor and a scoreboard in UVM methodology? 
  17. What is meant by factory and what is its importance? 
  18. What is the difference between creating an object using new() and create()? 
  19. What are the difference phases in UVM and what is the order of their execution? 
  20. What are objections and how are they useful? 
  21. How can you implement a simulation timeout mechanism using UVM methodology? 
  22. What is meant by factory override and what are different types of overriding possible with UVM factory? 
  23. What is a virtual sequence and where do we use a virtual sequence?
    Answer
  24. What are its benefits? What is uvm_config_db and what is it used for? 
  25. Why should any uvm component be registered with factory?