Tutorials

Learn More

Adder Design

Adder design produces the resultant addition of two variables on the positive edge of the clock. A reset signal is used to clear ‘out’ signal to 0.
Note: Adder can be easily developed with combinational logic. A clock and reset are introduced to have the flavor of a clock and reset in testbench code.

module adder(input clk, reset, input [7:0] in1, in2, output reg [8:0] out);
  always@(posedge clk or posedge reset) begin 
    if(reset) out <= 0;
    else out <= in1 + in2;
  end
endmodule

Testbench Code

SV Testbench Example

Transaction

The transaction is a packet that is driven to the DUT or monitored by the monitor as a pin-level activity.

class transaction;
  rand bit [7:0] ip1, ip2;
  bit [8:0] out;
   
  constraint ip_c {ip1 < 100; ip2 < 100;}
 
endclass

Generator

The generator creates or generates randomized transactions or stimuli and passes them to the driver.

class generator;
  int count;
  mailbox gen_to_drv;
  transaction tr;
  
  function new(mailbox gen_to_drv);
    this.gen_to_drv = gen_to_drv;
  endfunction
  
  task run;
    repeat(count) begin
      tr = new();
      void'(tr.randomize());
      gen_to_drv.put(tr);
    end
  endtask
endclass

Driver

The driver interacts with DUT. It receives randomized transactions from the generator and drives them to the driven as a pin level activity.

class driver;
  virtual add_if vif;
  mailbox gen_to_drv;
  transaction tr;
  
  function new(mailbox gen_to_drv, virtual add_if vif);
    this.gen_to_drv = gen_to_drv;
    this.vif = vif;
  endfunction
  
  task run;
    forever begin
      // Driver to the DUT
      @(posedge vif.clk);
      gen_to_drv.get(tr);
      
      vif.ip1 <= tr.ip1;
      vif.ip2 <= tr.ip2;
      @(posedge vif.clk);
      tr.out <= vif.out;
    end
  endtask
endclass

Monitor

The monitor observes pin-level activity on the connected interface at the input and output of the design.

class monitor;
  virtual add_if vif;
  mailbox mon_to_sb;
  
  function new(mailbox mon_to_sb, virtual add_if vif);
    this.vif = vif;
    this.mon_to_sb = mon_to_sb;
  endfunction
  
  task run;
    forever begin
      transaction mon_tr;
      wait(!vif.reset);
      @(posedge vif.clk);
      mon_tr = new();
      mon_tr.ip1 = vif.ip1;
      mon_tr.ip2 = vif.ip2;
      @(posedge vif.clk);
      mon_tr.out = vif.out;
      mon_to_sb.put(mon_tr);
    end
  endtask
endclass

Agent

An agent is a container that holds the generator, driver, and monitor.

class agent;
  driver drv;
  monitor mon;
  generator gen;
  
  mailbox gen_to_drv;
  virtual add_if vif;
  
  function new(virtual add_if vif, mailbox mon_to_sb);
    gen_to_drv = new();
   
    drv = new(gen_to_drv, vif);
    mon = new(mon_to_sb, vif);
    gen = new(gen_to_drv);
  endfunction

  task run();
    fork
      drv.run();
      mon.run();
      gen.run();
    join_any
  endtask
  
endclass

Scoreboard

The scoreboard receives the transaction packet from the monitor and compares it with the reference model.

class scoreboard;
  int compare_cnt;
  mailbox mon_to_sb;
  
  function new(mailbox mon_to_sb);
    this.mon_to_sb = mon_to_sb;
  endfunction
  
  task run;
    forever begin
      transaction tr;
      tr = new();
      mon_to_sb.get(tr);     
      if(tr.ip1 + tr.ip2 == tr.out) begin
        $display("Matched: ip1 = %0d, ip2 = %0d, out = %0d", tr.ip1, tr.ip2, tr.out);
      end
      else begin
        $display("NOT matched: ip1 = %0d, ip2 = %0d, out = %0d", tr.ip1, tr.ip2, tr.out);
      end
      compare_cnt++;
    end
  endtask
endclass

Environment

An environment allows a well-mannered hierarchy and container for agents, scoreboards.

class env;
  agent agt;
  scoreboard sb;
 
  mailbox mon_to_sb;
  function new(virtual add_if vif);
    mon_to_sb = new();
    agt = new(vif, mon_to_sb);
    sb = new(mon_to_sb);
  endfunction

  task run();
    fork
      agt.run();
      sb.run();
    join_any
    wait(agt.gen.count == sb.compare_cnt);
    $finish;
  endtask
endclass

Test

The test is at the top of the hierarchy that initiates the environment component construction and connection between them. 

program base_test(add_if vif);
  env env_o;
  
  initial begin
    env_o = new(vif);
    env_o.agt.gen.count = 5;
    env_o.run();
  end
endprogram

Testbench top

The testbench top is a top-level component that includes interface and DUT instances. It connects design with the testbench.

module tb_top;
  bit clk;
  bit reset;
  always #2 clk = ~clk;
  
  add_if vif(clk, reset);
  adder DUT(.clk(vif.clk),.reset(vif.reset),.in1(vif.ip1),.in2(vif.ip2),.out(vif.out));
  base_test t1(vif);
  
  initial begin
    clk = 0;
    reset = 1;
    #5; 
    reset = 0;
  end
endmodule

Output:

Matched: ip1 = 44, ip2 = 30, out = 74
Matched: ip1 = 86, ip2 = 27, out = 113
Matched: ip1 = 63, ip2 = 2, out = 65
Matched: ip1 = 99, ip2 = 11, out = 110
Matched: ip1 = 79, ip2 = 37, out = 116

System Verilog Tutorials