image/svg+xml
  • Contents
      • Back
      • Digital Basics
      • Verilog
      • Verification
      • SystemVerilog
      • UVM
Most Popular
Verification
  Testbench Evolution
  Constraint Random Verification
  Verification Techniques
  Verification Plan
  Code Coverage

Verilog
  Data Types
  Basic Constructs
  Behavioral Modeling
  Gate Modeling
  Simulation Basics
  Design Examples

SystemVerilog
  Data Types
  Class
  Interface
  Constraints and more!
  Testbench Examples

UVM
  Sequences
  Testbench Components
  TLM Tutorial
  Register Model Tutorial
  Testbench Examples

Digital Fundamentals
  Binary Arithmetic
  Boolean Logic
  Karnaugh Maps
  Combinational Logic
  Sequential Logic




Verilog Full Adder

An adder is a digital component that performs addition of two numbers. Its the main component inside an ALU of a processor and is used to increment addresses, table indices, buffer pointers and in a lot of other places where addition is required.

A full adder adds a carry input along with other input binary numbers to produce a sum and a carry output.

Truth Table

ABCinCoutSum
00000
00101
01001
01110
10001
10110
11010
11111

Design

An example of a 4-bit adder is shown below which accepts two binary numbers through the signals a and b which are both 4-bits wide. Since an adder is a combinational circuit, it can be modeled in Verilog using a continuous assignment with assign or an always block with a sensitivity list that comprises of all inputs. The code shown below is that of the former approach.


module fulladd (  input [3:0] a,
                  input [3:0] b,
                  input c_in,
                  output c_out,
                  output [3:0] sum);

   assign {c_out, sum} = a + b + c_in;
endmodule

The code shown below uses an always block which gets executed whenever any of its inputs change value.


module fulladd (  input [3:0] a,
                  input [3:0] b,
                  input c_in,
                  output reg c_out,
                  output reg [3:0] sum);

	always @ (a or b or c_in) begin
  	{c_out, sum} = a + b + c_in; 	
  end
endmodule

Hardware Schematic

Testbench


module tb_fulladd;
	// 1. Declare testbench variables
   reg [3:0] a;
   reg [3:0] b;
   reg c_in;
   wire [3:0] sum;
   integer i;

	// 2. Instantiate the design and connect to testbench variables
   fulladd  fa0 ( .a (a),
                  .b (b),
                  .c_in (c_in),
                  .c_out (c_out),
                  .sum (sum));

	// 3. Provide stimulus to test the design
   initial begin
      a <= 0;
      b <= 0;
      c_in <= 0;
      
      $monitor ("a=0x%0h b=0x%0h c_in=0x%0h c_out=0x%0h sum=0x%0h", a, b, c_in, c_out, sum);

		// Use a for loop to apply random values to the input
      for (i = 0; i < 5; i = i+1) begin
         #10 a <= $random;
             b <= $random;
         		 c_in <= $random;
      end
   end
endmodule

Note that when a and b add up to give a number more than 4 bits wide, the sum rolls over to zero and c_out becomes 1. For example, the line highlighted in yellow adds up to give 0x11 and the lower 4 bits get assigned to sum and bit#4 to c_out .

 Simulation Log
ncsim> run
a=0x0 b=0x0 c_in=0x0 c_out=0x0 sum=0x0
a=0x4 b=0x1 c_in=0x1 c_out=0x0 sum=0x6
a=0x3 b=0xd c_in=0x1 c_out=0x1 sum=0x1
a=0x5 b=0x2 c_in=0x1 c_out=0x0 sum=0x8
a=0xd b=0x6 c_in=0x1 c_out=0x1 sum=0x4
a=0xd b=0xc c_in=0x1 c_out=0x1 sum=0xa
ncsim: *W,RNQUIE: Simulation is complete.

Verilog 4 to 1 Multiplexer/Mux

What is a mux or multiplexer ?

A multiplexer or mux in short, is a digital element that transfers data from one of the N inputs to the output based on the select signal. The case shown below is when N equals 4. For example, a 4 bit multiplexer would have N inputs each of 4 bits where each input can be transferred to the output by the use of a select signal.

4x1 Mux

sel is a 2-bit input and can have four values. Each value on the select line will allow one of the inputs to be sent to output pin out.

	sel     a     b     c     d     out
	 0     3    7     1     9    3
	 1      3    7    1     9    7
	 2      3     7    1    9    1
	 3      3     7     1    9   9

A 4x1 multiplexer can be implemented in multiple ways and here you'll see two of the most common ways:

  • Using an assign statement
  • Using a case statement

Using assign statement


module mux_4to1_assign ( input [3:0] a,                 // 4-bit input called a
                         input [3:0] b,                 // 4-bit input called b
                         input [3:0] c,                 // 4-bit input called c
                         input [3:0] d,                 // 4-bit input called d
                         input [1:0] sel,               // input sel used to select between a,b,c,d
                         output [3:0] out);             // 4-bit output based on input sel

   // When sel[1] is 0, (sel[0]? b:a) is selected and when sel[1] is 1, (sel[0] ? d:c) is taken
   // When sel[0] is 0, a is sent to output, else b and when sel[0] is 0, c is sent to output, else d
   assign out = sel[1] ? (sel[0] ? d : c) : (sel[0] ? b : a); 
   
endmodule

The module called mux_4x1_assign has four 4-bit data inputs, one 2-bit select input and one 4-bit data output. The multiplexer will select either a , b, c, or d based on the select signal sel using the assign statement.

4 to 1 multiplexer waveform

Using case statement

Note that the signal out is declared as a reg type because it is used in a procedural block like always.


module mux_4to1_case ( input [3:0] a,                 // 4-bit input called a
                       input [3:0] b,                 // 4-bit input called b
                       input [3:0] c,                 // 4-bit input called c
                       input [3:0] d,                 // 4-bit input called d
                       input [1:0] sel,               // input sel used to select between a,b,c,d
                       output reg [3:0] out);         // 4-bit output based on input sel
                  
   // This always block gets executed whenever a/b/c/d/sel changes value
   // When that happens, based on value in sel, output is assigned to either a/b/c/d
   always @ (a or b or c or d or sel) begin
      case (sel)
         2'b00 : out <= a;
         2'b01 : out <= b;
         2'b10 : out <= c;
         2'b11 : out <= d;
      endcase
   end
endmodule

The module called mux_4x1_case has four 4-bit data inputs, one 2-bit select input and one 4-bit data output. The multiplexer will select either a , b, c, or d based on the select signal sel using the case statement.

Hardware Schematic

Both types of multiplexer models get synthesized into the same hardware as shown in the image below.

Testbench


module tb_4to1_mux;

   // Declare internal reg variables to drive design inputs
   // Declare wire signals to collect design output
   // Declare other internal variables used in testbench
   reg [3:0] a;
   reg [3:0] b;
   reg [3:0] c;
   reg [3:0] d;
   wire [3:0] out;
   reg [1:0] sel;
   integer i;
   
   // Instantiate one of the designs, in this case, we have used the design with case statement
   // Connect testbench variables declared above with those in the design
   mux_4to1_case  mux0 (   .a (a),
                           .b (b),
                           .c (c),
                           .d (d),
                           .sel (sel),
                           .out (out));

   // This initial block is the stimulus
   initial begin
      // Launch a monitor in background to display values to log whenever a/b/c/d/sel/out changes
      $monitor ("[%0t] sel=0x%0h a=0x%0h b=0x%0h c=0x%0h d=0x%0h out=0x%0h", $time, sel, a, b, c, d, out);
   
   	  // 1. At time 0, drive random values to a/b/c/d and keep sel = 0
      sel <= 0;
      a <= $random;
      b <= $random;
      c <= $random;
      d <= $random;
      
      // 2. Change the value of sel after every 5ns
      for (i = 1; i < 4; i=i+1) begin
         #5 sel <= i;
      end
      
      // 3. After Step2 is over, wait for 5ns and finish simulation
      #5 $finish;
   end
endmodule
 Simulation Log
ncsim> run
[0] sel=0x0 a=0x4 b=0x1 c=0x9 d=0x3 out=0x4
[5] sel=0x1 a=0x4 b=0x1 c=0x9 d=0x3 out=0x1
[10] sel=0x2 a=0x4 b=0x1 c=0x9 d=0x3 out=0x9
[15] sel=0x3 a=0x4 b=0x1 c=0x9 d=0x3 out=0x3
Simulation complete via $finish(1) at time 20 NS + 0
4 to 1 multiplexer waveform

Verilog Ripple Counter

A ripple counter is an asynchronous counter in which the all the flops except the first are clocked by the output of the preceding flop.

ripple-counter

Design


module dff (   input d,
               input clk,
               input rstn,
               output reg q,
               output qn);
   always @ (posedge clk or negedge rstn)
      if (!rstn)
         q <= 0;
      else  
         q <= d;

   assign qn = ~q;
endmodule

module ripple ( input clk,
                input rstn,
                output [3:0] out);
   wire  q0;
   wire  qn0;
   wire  q1;
   wire  qn1;
   wire  q2;
   wire  qn2;
   wire  q3;
   wire  qn3;
   
   dff   dff0 ( .d (qn0), 
                .clk (clk),
                .rstn (rstn),
                .q (q0),
                .qn (qn0));

   dff   dff1 ( .d (qn1), 
                .clk (q0),
                .rstn (rstn),
                .q (q1),
                .qn (qn1));

   dff   dff2 ( .d (qn2), 
                .clk (q1),
                .rstn (rstn),
                .q (q2),
                .qn (qn2));

   dff   dff3 ( .d (qn3), 
                .clk (q2),
                .rstn (rstn),
                .q (q3),
                .qn (qn3));

   assign out = {qn3, qn2, qn1, qn0};

endmodule

Hardware Schematic

Testbench


module tb_ripple;
   reg clk;
   reg rstn;
   wire [3:0] out;

   ripple r0   (  .clk (clk),
                  .rstn (rstn),
                  .out (out));

   always #5 clk = ~clk;

   initial begin
      rstn <= 0;
      clk <= 0;

      repeat (4) @ (posedge clk);
      rstn <= 1;

      repeat (25) @ (posedge clk);
      $finish;
   end
endmodule

UVM Sequence Arbitration

When multiple sequences try to access a single driver, the sequencer that is executing sequences schedules them in a certain order through a process called arbitration. The sequencer can be configured to grant driver access to certain sequences over others based on certain criteria called as arbitration modes.

Example

To understand the different modes of arbitration, lets first build a testbench containing an environment with a sequencer and driver. Note that sequences that do not require driver access or execute sequence items using start_item and finish_item do not have to undergo arbitration. So if multiple plain sequences are started on a sequencer, they will be executed in parallel.

1. Data class

The class called base_class represents a data object that will be sent to the driver so that it can convert this into protocol level pin toggles at the DUT interface. This class definition is kept simple for our purpose to illustrate the arbitration scheme


class base_pkt extends uvm_sequence_item;
  `uvm_object_utils(base_pkt)
  function new(string name = "base_pkt");
    super.new(name);
  endfunction
  
  rand bit[7:0] addr;
endclass

2. Driver

Assume a driver accepts a data object of type base_pkt and toggles pins of the DUT.


class base_driver extends uvm_driver #(base_pkt);
  `uvm_component_utils(base_driver)
  function new(string name = "base_driver", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  // Assume that the packet is driven across to the DUT within 10ns
  // after which the driver calls item_done()
  virtual task run_phase(uvm_phase phase);
    base_pkt m_pkt;
    forever begin
      seq_item_port.get_next_item(m_pkt);
      #10 `uvm_info ("DRV", $sformatf("Drive packet with addr=0x%0h", m_pkt.addr), UVM_LOW)
      seq_item_port.item_done();
    end
  endtask
endclass

3. Environment

The environment class instantiates a sequencer and driver and makes the connection between them.


class base_env extends uvm_env;
  `uvm_component_utils(base_env)
  function new(string name="base_env", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  base_driver  		 		m_drv;
  uvm_sequencer#(base_pkt)  m_seqr;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_drv = base_driver::type_id::create("m_drv", this);
    m_seqr = uvm_sequencer#(base_pkt)::type_id::create("m_seqr", this);
  endfunction
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    m_drv.seq_item_port.connect(m_seqr.seq_item_export);
  endfunction
endclass

4. Sequence

The sequence class called base_seq creates a data packet and is executed by the sequencer defined above. The data packet will be sent to the driver and will be blocked until the driver calls item_done method.

Since multiple instances of this sequence will be started on a single sequencer, a variable called seq_num is declared to distinguish between sequences. The address of the packet is also made unique to identify which sequence is responsible for sending data through the driver.


class base_seq extends uvm_sequence;
  `uvm_object_utils(base_seq)
  function new(string name="base_seq");
    super.new(name);
  endfunction
  
  int seq_num;
  
  virtual task body();
    base_pkt m_pkt = base_pkt::type_id::create("m_pkt");
    `uvm_info(get_name(), $sformatf("Started body"), UVM_LOW)
    start_item(m_pkt);
    m_pkt.randomize() with { addr == seq_num; };
    finish_item(m_pkt);
    #10 `uvm_info(get_name(), $sformatf("Ended body"), UVM_LOW)
  endtask
endclass

5. Test

A base test class is defined below that builds the environment and all sequences. It also has an additional function to display the current arbitration scheme of the sequencer within environment. Based on the arbitration scheme, the sequencer follows a certain of order of granting access to all sequences to the driver.


class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  function new(string name="base_test", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  base_seq 		m_seq[6];
  base_env 		m_env;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
   foreach(m_seq[i]) begin
      m_seq[i] = base_seq::type_id::create($sformatf("m_seq_%0d", i));
      m_seq[i].seq_num = i;
    end
    m_env = base_env::type_id::create("m_env", this);    
  endfunction
  
  virtual function void show_arb_cfg();
	UVM_SEQ_ARB_TYPE  cur_arb;
        
    cur_arb = m_env.m_seqr.get_arbitration();
    `uvm_info("TEST", $sformatf("Seqr set to %s", cur_arb.name()), UVM_LOW)
  endfunction
endclass

UVM_SEQ_ARB_FIFO

This is the default arbitration mode where the UVM sequencer starts them regardless of their priorities.

Without priority


class arb_fifo_test extends base_test;
  `uvm_component_utils(arb_fifo_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_FIFO);
    show_arb_cfg();
    
    fork
      m_seq[0].start(m_env.m_seqr);
      m_seq[1].start(m_env.m_seqr);
      m_seq[2].start(m_env.m_seqr);
      m_seq[3].start(m_env.m_seqr);
    join
    phase.drop_objection(this);
  endtask
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

With priority

Lets add some priorities to the sequence when they are started on the sequencer and see how they behave.


class arb_fifo_test extends base_test;
  `uvm_component_utils(arb_fifo_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_FIFO);
    show_arb_cfg();

    fork
      m_seq[0].start(m_env.m_seqr, .this_priority(100));
      m_seq[1].start(m_env.m_seqr, .this_priority(200));
      m_seq[2].start(m_env.m_seqr, .this_priority(300));
      m_seq[3].start(m_env.m_seqr, .this_priority(400));
    join
    phase.drop_objection(this);
  endtask
endclass

Note that priorities do not take effect in UVM_SEQ_ARB_FIFO arbitration mode

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

UVM_SEQ_ARB_RANDOM

In here the sequencer will randomly start sequences regardless of their priorities.

Without priority


class arb_random_test extends base_test;
  `uvm_component_utils(arb_random_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_RANDOM);
    show_arb_cfg();
    
    fork
      m_seq[0].start(m_env.m_seqr);
      m_seq[1].start(m_env.m_seqr);
      m_seq[2].start(m_env.m_seqr);
      m_seq[3].start(m_env.m_seqr);
    join
    phase.drop_objection(this);
  endtask
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

With priority

Lets add some priorities to the sequence when they are started on the sequencer and see how they behave.


class arb_random_test extends base_test;
  `uvm_component_utils(arb_random_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_RANDOM);
    show_arb_cfg();
    
    fork
      m_seq[0].start(m_env.m_seqr, .this_priority(100));
      m_seq[1].start(m_env.m_seqr, .this_priority(200));
      m_seq[2].start(m_env.m_seqr, .this_priority(300));
      m_seq[3].start(m_env.m_seqr, .this_priority(400));
    join
    phase.drop_objection(this);
  endtask
endclass

Note that priorities do not take effect in UVM_SEQ_ARB_RANDOM arbitration mode

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

UVM_SEQ_ARB_STRICT_FIFO

This mode will always grant the sequence with highest priority first. Multiple sequences with the same priority will be arbitrated similar to a FIFO. For this test, let us increase the number of sequences just to have multiple sequences on the same priority.

Without priority


class arb_strict_fifo_test extends base_test;
  `uvm_component_utils(arb_strict_fifo_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
    show_arb_cfg();

    fork
      m_seq[0].start(m_env.m_seqr);
      m_seq[3].start(m_env.m_seqr);
	  m_seq[1].start(m_env.m_seqr);
      m_seq[2].start(m_env.m_seqr);
      m_seq[4].start(m_env.m_seqr);
      m_seq[5].start(m_env.m_seqr);
    join
    phase.drop_objection(this);
  endtask
endclass

See that output is the same as UVM_SEQ_ARB_FIFO arbitration mode when no priorities are specified.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

With priority


class arb_strict_fifo_test extends base_test;
  `uvm_component_utils(arb_strict_fifo_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
    show_arb_cfg();

    fork
      m_seq[0].start(m_env.m_seqr, .this_priority(100));
      m_seq[1].start(m_env.m_seqr, .this_priority(200));
      m_seq[2].start(m_env.m_seqr, .this_priority(200));
      m_seq[3].start(m_env.m_seqr, .this_priority(200));
	  m_seq[4].start(m_env.m_seqr, .this_priority(200));
      m_seq[5].start(m_env.m_seqr, .this_priority(400));      
    join

    phase.drop_objection(this);
  endtask
endclass

Note that priorities affect output in UVM_SEQ_ARB_STRICT_FIFO mode.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

UVM_SEQ_ARB_STRICT_RANDOM

This mode will always grant the sequence with highest priority first. Multiple sequences with the same priority will be randomly picked. For this test, let us increase the number of sequences just to have more sequences on the same priority.

Without priority


class arb_strict_random_test extends base_test;
  `uvm_component_utils(arb_strict_random_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_RANDOM);
    show_arb_cfg();

    fork
      m_seq[0].start(m_env.m_seqr);
      m_seq[3].start(m_env.m_seqr);
	  m_seq[1].start(m_env.m_seqr);
      m_seq[2].start(m_env.m_seqr);
      m_seq[4].start(m_env.m_seqr);
      m_seq[5].start(m_env.m_seqr);
    join

    phase.drop_objection(this);
  endtask
endclass

Note that this is similar to UVM_SEQ_ARB_RANDOM mode when there are no priorities.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

With priority


class arb_strict_random_test extends base_test;
  `uvm_component_utils(arb_strict_random_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);

    m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_RANDOM);
    show_arb_cfg();

    fork
      m_seq[0].start(m_env.m_seqr, .this_priority(100));
      m_seq[1].start(m_env.m_seqr, .this_priority(200));
      m_seq[2].start(m_env.m_seqr, .this_priority(200));
      m_seq[3].start(m_env.m_seqr, .this_priority(200));
      m_seq[4].start(m_env.m_seqr, .this_priority(200));
      m_seq[5].start(m_env.m_seqr, .this_priority(400));
    join

    phase.drop_objection(this);
  endtask
endclass

Note that m_seq_1 through m_seq_4 that had the same priority come out in a random order. Compare this with UVM_SEQ_ARB_STRICT_FIFO mode, where these sequences came out in a FIFO order.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

UVM_SEQ_ARB_WEIGHTED

There is a higher chance of picking higher priority sequences in this mode.

UVM_SEQ_ARB_USER

If none of the above modes satisfies your requirement, then you can create a user defined arbitration scheme. All you need to do is to create a new sequencer extended from uvm_sequencer and define its user_priority_arbitration function.

UVM config database

UVM has an internal database table in which we can store values under a given name and can be retrieved later by some other testbench component. The uvm_config_db class provides a convenience interface on top of the uvm_resource_db to simplify the basic interface used for uvm_component instances. Note that all the functions are static and must be called using the :: scope operator.

Such a configuration database allows us to store different configuration settings under different names that could potentially configure testbench components when required without modifying the actual testbench code. For example, to turn on functional coverage for an agent, we would simply have to give the path to that agent and set a variable within the configuration database to value 1. The agent could check for the value under this variable and start collecting coverage if it is turned on.

config-cell

set()


	static function void set (  uvm_component cntxt,
	                            string        inst_name,
	                            string        field_name,
	                            T             value);

Use this static function of the class uvm_config_db to set a variable in the configuration database. In the example below, set() function will set a variable of name cov_enable at the path uvm_test_top.m_env.m_apb_agent with value 1.


	virtual function void build_phase (uvm_phase phase);
		...
		uvm_config_db #(int) :: set (null, "uvm_test_top.m_env.m_apb_agent", "cov_enable", 1);
		...
	endfunction

Read more: UVM config database

  1. UVM Virtual Sequence
  2. UVM Virtual Sequencer
  3. UVM Sequence [uvm_sequence]
  4. Subscriber [uvm_subscriber]
  5. JK Flip Flop

Page 35 of 63

  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
Interview Questions
  Verilog Interview Set 1
  Verilog Interview Set 2
  Verilog Interview Set 3
  Verilog Interview Set 4
  Verilog Interview Set 5

  SystemVerilog Interview Set 1
  SystemVerilog Interview Set 2
  SystemVerilog Interview Set 3
  SystemVerilog Interview Set 4
  SystemVerilog Interview Set 5

  UVM Interview Set 1
  UVM Interview Set 2
  UVM Interview Set 3
  UVM Interview Set 4
Related Topics
  Digital Fundamentals
  Verilog Tutorial

  Verification
  SystemVerilog Tutorial
  UVM Tutorial
  • Verilog Testbench
  • Verilog Coding Style Effect
  • Verilog Conditional Statements
  • Verilog Interview Set 10
  • Synchronous FIFO
  • SystemVerilog Interview Set 10
  • SystemVerilog Interview Set 9
  • SystemVerilog Interview Set 8
  • SystemVerilog Interview Set 7
  • SystemVerilog Interview Set 6
  • UVM Singleton Object
  • UVM Component [uvm_component]
  • UVM Object [uvm_object]
  • UVM Root [uvm_root]
  • UVM Interview Set 4
© 2015 - 2023 ChipVerify
Terms and Conditions | DMCA.com Protection Status