‘ADDER’ TestBench Without Monitor, Agent and Scoreboard
Table of Contents
TestBench Architecture
Transaction Class
- Fields required to generate the stimulus are declared in the transaction class.
- Transaction class can also be used as a placeholder for the activity monitored by the monitor on DUT signals.
- So, the first step is to declare the ‘Fields‘ in the transaction class.
- Below are the steps to write the transaction class.
1. Declaring the fields.
class transaction; //declaring the transaction items bit [3:0] a; bit [3:0] b; bit [6:0] c; endclass
2. To generate the random stimulus, declare the fields as ‘rand‘.
class transaction; //declaring the transaction items rand bit [3:0] a; rand bit [3:0] b; bit [7:0] c; endclass
3. Adding display() method to display Transaction properties.
class transaction; //declaring the transaction items rand bit [3:0] a; rand bit [3:0] b; bit [6:0] c; function void display(string name); $display("-------------------------"); $display("- %s ",name); $display("-------------------------"); $display("- a = %0d, b = %0d",a,b); $display("- c = %0d",c); $display("-------------------------"); endfunction endclass
Generator Class
Generator class is responsible for,
- Generating the stimulus by randomizing the transaction class
- Sending the randomized class to driver
class generator; ------ endclass
1. Declare the transaction class handle,
class generator; //declaring transaction class rand transaction trans; endclass
2. ‘Randomize’ the transaction class,
class generator; //declaring transaction class rand transaction trans; //main task, generates(create and randomizes) the packets and puts into mailbox task main(); trans = new(); if( !trans.randomize() ) $fatal("Gen:: trans randomization failed"); gen2driv.put(trans); endtask endclass
3. Adding Mailbox and event,
Mailbox is used to send the randomized transaction to Driver.
Event to indicate the end of packet generation.
This involves,
- Declaring the Mailbox and Event
- Getting the Mailbox handle from the env class ( because the same mailbox will be shared across generator and driver).
class generator; //declaring transaction class rand transaction trans; //declaring mailbox mailbox gen2driv; //event, to indicate the end of transaction generation event ended; //constructor function new(mailbox gen2driv); //getting the mailbox handle from env this.gen2driv = gen2driv; endfunction //main task, generates(create and randomizes) the packets and puts into mailbox task main(); trans = new(); if( !trans.randomize() ) $fatal("Gen:: trans randomization failed"); gen2driv.put(trans); -> ended; //triggering indicatesthe end of generation endtask endclass
4. Adding a variable to control the number of packets to be created,
class generator; //declaring transaction class rand transaction trans; //declaring mailbox mailbox gen2driv; //event, to indicate the end of transaction generation event ended; //repeat count, to specify number of items to generate int repeat_count; //constructor function new(mailbox gen2driv); //getting the mailbox handle from env this.gen2driv = gen2driv; endfunction //main task, generates(create and randomizes) the repeat_count number of transaction packets and puts into mailbox task main(); repeat(repeat_count) begin trans = new(); if( !trans.randomize() ) $fatal("Gen:: trans randomization failed"); gen2driv.put(trans); end -> ended; //triggering indicatesthe end of generation endtask endclass
5. Adding an event to indicate the completion of the generation process, the event will be triggered on the completion of the Generation process.
class generator; //declaring transaction class rand transaction trans; //declaring mailbox mailbox gen2driv; //repeat count, to specify number of items to generate int repeat_count; //event, to indicate the end of transaction generation event ended; //constructor function new(mailbox gen2driv); //getting the mailbox handle from env this.gen2driv = gen2driv; endfunction //main task, generates(create and randomizes) the repeat_count number of transaction packets and puts into mailbox task main(); repeat(repeat_count) begin trans = new(); if( !trans.randomize() ) $fatal("Gen:: trans randomization failed"); gen2driv.put(trans); end -> ended; //triggering indicatesthe end of generation endtask endclass
Interface
Interface will group the signals.
This is a simple interface without modport and clocking block.
interface intf(input logic clk,reset); ---- endinterface
1. Complete Interface code,
interface intf(input logic clk,reset); //declaring the signals logic valid; logic [3:0] a; logic [3:0] b; logic [6:0] c; endinterface
Driver Class
Driver class is responsible for,
- receive the stimulus generated from the generator and drive to DUT by assigning transaction class values to interface signals.
class driver; ---- endclass
1. Declare interface and mailbox, Get the interface and mailbox handle through a constructor.
//creating virtual interface handle virtual intf vif; //creating mailbox handle mailbox gen2driv; //constructor function new(virtual intf vif,mailbox gen2driv); //getting the interface this.vif = vif; //getting the mailbox handle from environment this.gen2driv = gen2driv; endfunction
2. Adding a reset task, which initializes the Interface signals to default values.
//Reset task, Reset the Interface signals to default/initial values task reset; wait(vif.reset); $display("[ DRIVER ] ----- Reset Started -----"); vif.a <= 0; vif.b <= 0; vif.valid <= 0; wait(!vif.reset); $display("[ DRIVER ] ----- Reset Ended -----"); endtask
3. Adding a drive task to drive the transaction packet to the interface signal.
//drive the transaction items to interface signals task drive; transaction trans; gen2driv.get(trans); @(posedge vif.clk); vif.valid <= 1; vif.a <= trans.a; vif.b <= trans.b; @(posedge vif.clk); vif.valid <= 0; trans.c <= vif.c; @(posedge vif.clk); trans.display("[ Driver ]"); no_transactions++; end endtask
4. Adding a local variable to track the number of packets driven, and increment the variable in the drive task.
(This
will be useful to end the test-case/Simulation. i.e compare the
generated pkt’s and driven pkt’s if both are same then end the
simulation)
//used to count the number of transactions int no_transactions; //drive the transaction items to interface signals task drive; ------ ------ no_transactions++; endtask
5. Complete driver code.
class driver; //used to count the number of transactions int no_transactions; //creating virtual interface handle virtual intf vif; //creating mailbox handle mailbox gen2driv; //constructor function new(virtual intf vif,mailbox gen2driv); //getting the interface this.vif = vif; //getting the mailbox handles from environment this.gen2driv = gen2driv; endfunction //Reset task, Reset the Interface signals to default/initial values task reset; wait(vif.reset); $display("[ DRIVER ] ----- Reset Started -----"); vif.a <= 0; vif.b <= 0; vif.valid <= 0; wait(!vif.reset); $display("[ DRIVER ] ----- Reset Ended -----"); endtask //drivers the transaction items to interface signals task main; forever begin transaction trans; gen2driv.get(trans); @(posedge vif.clk); vif.valid <= 1; vif.a <= trans.a; vif.b <= trans.b; @(posedge vif.clk); vif.valid <= 0; trans.c <= vif.c; @(posedge vif.clk); trans.display("[ Driver ]"); no_transactions++; end endtask endclass
Environment
Environment is container class contains Mailbox, Generator and Driver.
Creates the mailbox, generator and driver shares the mailbox handle across the Generator and Driver.
class environment; --- endclass
1. Declare the handles,
//generator and driver instance generator gen; driver driv; //mailbox handle's mailbox gen2driv; //virtual interface virtual intf vif;
2. In Construct Method, Create
- Mailbox
- Generator
- Driver
and pass the interface handle through the new() method.
//constructor function new(virtual intf vif); //get the interface from test this.vif = vif; //creating the mailbox (Same handle will be shared across generator and driver) gen2driv = new(); //creating generator and driver gen = new(gen2driv); driv = new(vif,gen2driv); endfunction
3. For better accessibility.
Generator and Driver activity can be divided and controlled in three methods.
- pre_test() – Method to call Initialization. i.e, reset method.
- test() – Method to call Stimulus Generation and Stimulus Driving.
- post_test() – Method to wait the completion of generation and driving.
task pre_test(); driv.reset(); endtask task test(); fork gen.main(); driv.main(); join_any endtask task post_test(); wait(gen.ended.triggered); wait(gen.repeat_count == driv.no_transactions); endtask
4. Add a run task to call the above methods,
call $finish after post_test() to end the simulation.
task run; pre_test(); test(); post_test(); $finish; endtask
5. Complete environment class code.
`include "transaction.sv" `include "generator.sv" `include "driver.sv" class environment; //generator and driver instance generator gen; driver driv; //mailbox handle's mailbox gen2driv; //virtual interface virtual intf vif; //constructor function new(virtual intf vif); //get the interface from test this.vif = vif; //creating the mailbox (Same handle will be shared across generator and driver) gen2driv = new(); //creating generator and driver gen = new(gen2driv); driv = new(vif,gen2driv); endfunction // task pre_test(); driv.reset(); endtask task test(); fork gen.main(); driv.main(); join_any endtask task post_test(); wait(gen.ended.triggered); wait(gen.repeat_count == driv.no_transactions); endtask //run task task run; pre_test(); test(); post_test(); $finish; endtask endclass
Test
Test code is written with the program block.
The test is responsible for,
- Creating the environment.
- Configuring the testbench i.e, setting the type and number of transactions to be generated.
- Initiating the stimulus driving.
program test; ---- endprogram
1. Declare and Create an environment,
//declaring environment instance environment env; initial begin //creating environment env = new(intf); end
2. Configure the number of transactions to be generated,
//setting the repeat count of generator as 10, means to generate 10 packets env.gen.repeat_count = 10;
3. Initiating the stimulus driving,
//calling run of env, it interns calls generator and driver main tasks. env.run();
4. Complete Test Code,
`include "environment.sv" program test(intf intf); //declaring environment instance environment env; initial begin //creating environment env = new(intf); //setting the repeat count of generator as 10, means to generate 10 packets env.gen.repeat_count = 10; //calling run of env, it interns calls generator and driver main tasks. env.run(); end endprogram
TestBench Top
- This is the topmost file, which connects the DUT and TestBench.
- TestBench top consists of DUT, Test and Interface instances.
- The interface connects the DUT and TestBench.
module tbench_top; --- endmodule
1. Declare and Generate the clock and reset,
//clock and reset signal declaration bit clk; bit reset; //clock generation always #5 clk = ~clk; //reset Generation initial begin reset = 1; #5 reset =0; end
2. Create Interface instance,
//creatinng instance of interface, in-order to connect DUT and testcase intf intf(clk,reset);
3. Create Design Instance and Connect Interface signals,
//DUT instance, interface signals are connected to the DUT ports adder DUT ( .clk(i_intf.clk), .reset(i_intf.reset), .a(i_intf.a), .b(i_intf.b), .valid(i_intf.valid), .c(i_intf.c) );
4. Create a test instance and Pass the interface handle,
//Testcase instance, interface handle is passed to test as an argument test t1(intf);
5. Add logic to generate the dump,
initial begin $dumpfile("dump.vcd"); $dumpvars; end
6. Complete testbench top code,
`include "interface.sv" `include "random_test.sv" module tbench_top; //clock and reset signal declaration bit clk; bit reset; //clock generation always #5 clk = ~clk; //reset Generation initial begin reset = 1; #5 reset =0; end //creatinng instance of interface, inorder to connect DUT and testcase intf i_intf(clk,reset); //Testcase instance, interface handle is passed to test as an argument test t1(i_intf); //DUT instance, interface signals are connected to the DUT ports adder DUT ( .clk(i_intf.clk), .reset(i_intf.reset), .a(i_intf.a), .b(i_intf.b), .valid(i_intf.valid), .c(i_intf.c) ); //enabling the wave dump initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule