You are on page 1of 30

Verification

Environment/TestBench for Memory


Model
SystemVerilog Verification Environment/TestBench for
Memory Model
The steps involved in the verification process are,

Creation of Verification plan


Testbench Architecture
Writing TestBench

Before writing/creating the verification plan need to know about design, so will go
through the design specification.

* In this example Design/DUT is Memory Model.

Memory Model Design Specification

SystemVerilog Memory Model Design

Signal Definition:

Signal Direction wrt


Name to Design Description

clk input clock signal


input
reset reset signal
input
addr[1:0] Address signal on which the address is
specified

1/3
wr_en input write enable signal, indicates the write
operation

rd_en input read enable signal, indicates the read


operation
wdata signal for write data
wdata[7:0] input
rdata signal for read data
rdata[7:0] output

Operations:

Write Operation:
address, wr_en, and wdata should be driven at the same clock cycle.

Read Operation:
address and rd_en should be driven on the same clock cycle, Design will respond with the
data in the next clock cycle.

Design Features,

The Memory model is capable of storing 8bits of data per address


location Reset values of each address memory location is ‘hFF

Creation of Verification plan


The verification plan is the list of scenarios need to be verified.
let’s list the few scenarios,

1. Write and Read to a particular memory location


Perform write to any memory location, read from the same memory location,
read data should be the same as written data
2. Write and Read to all memory locations
Perform write and read to all the memory locations (as the address is 2bit
width the possible address are 2‘b00, 2’b01, 2’b10, and 2’b11)
3. Default memory value check
Check default memory values. (before writing any locations, do read operation
we should get default values as ‘hFF)
4. Reset in Middle of Write/Read Operation
Assert reset in between write/read operation and check for default values.
(after writing to few locations assert the reset and perform read operation, we
should get default memory location value ‘hFF)

2/3
TestBench Hierarchy and Architecture

SystemVerilog testbench hierarchy to verify “Memory Model”

SystemVerilog Testbench block diagram

Writing Verification Environment/TestBench


For simplicity will write the two Testbenches,

3/3
SystemVerilog TestBench Example 01
verificationguide.com/systemverilog-examples/systemverilog-testbench-example-01

Memory Model TestBench Without Monitor, Agent, and Scoreboard

TestBench Architecture

SystemVerilog TestBench

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 a transaction class

1. Declaring the fields.

class transaction;

//declaring the transaction items


bit [1:0] addr;
bit wr_en;
bit rd_en;
bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;

endclass

2. To generate the random stimulus, declare the fields as rand.

1/17
class transaction;

//declaring the transaction items


rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;

endclass

3. Either write or read operation will be performed at once, so wr_en or rd_en is


generated by .adding constraint

class transaction;

//declaring the transaction items


rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;

//constaint, to generate any one among write and read


constraint wr_rd_c { wr_en != rd_en; };

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. Randomizethe transaction class,

2/17
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. Mailbox is used to send the randomized transaction to Driver,

This involves,
Declaring the Mailbox
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;

//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);
endtask

endclass

4. Adding a variable to control the number of packets to be created,

3/17
class generator;

//declaring transaction class


rand transaction trans;

//declaring mailbox
mailbox gen2driv;

//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
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.

4/17
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
event ended;

//constructor
function new(mailbox gen2driv,event ended);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
this.ended = ended;
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;
endtask
endclass

Interface:
Interface will group the signals, specifies the direction (Modport) and Synchronize the
signals(Clocking Block).

interface mem_intf(input logic clk,reset);


----
endinterface

1. Driver Clocking Block,

//driver clocking block


clocking driver_cb @(posedge clk);
default input #1 output #1;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;
endclocking

5/17
2. Monitor Clocking Block,

//monitor clocking block


clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;
endclocking

3. Driver and Monitor modport,

//driver modport
modport DRIVER (clocking driver_cb,input clk,reset);

//monitor modport
modport MONITOR (clocking monitor_cb,input clk,reset);

4. Complete Interface code,

6/17
interface mem_intf(input logic clk,reset);

//declaring the signals


logic [1:0] addr;
logic wr_en;
logic rd_en;
logic [7:0] wdata;
logic [7:0] rdata;

//driver clocking block


clocking driver_cb @(posedge clk);
default input #1 output #1;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;
endclocking

//monitor clocking block


clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;
endclocking

//driver modport
modport DRIVER (clocking driver_cb,input clk,reset);

//monitor modport
modport MONITOR (clocking monitor_cb,input clk,reset);

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 the
constructor

7/17
//creating virtual interface handle
virtual mem_intf mem_vif;

//creating mailbox handle


mailbox gen2driv;

//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction

2. Adding a reset task, which initializes the Interface signals to default values

For simplicity, define is used to access interface signals.

`define DRIV_IF mem_vif.DRIVER.driver_cb

`DRIV_IF will point to mem_vif.DRIVER.driver_cb

//Reset task, Reset the Interface signals to default/initial values


task reset;
wait(mem_vif.reset);
$display("--------- [DRIVER] Reset Started ---------");
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
`DRIV_IF.addr <= 0;
`DRIV_IF.wdata <= 0;
wait(!mem_vif.reset);
$display("--------- [DRIVER] Reset Ended ---------");
endtask

3. Adding a drive task to drive the transaction packet to the interface signal

8/17
//drive the transaction items to interface signals
task drive;
forever begin
transaction trans;
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
gen2driv.get(trans);
$display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.addr <= trans.addr;
if(trans.wr_en) begin
`DRIV_IF.wr_en <= trans.wr_en;
`DRIV_IF.wdata <= trans.wdata;
$display("\tADDR = %0h \tWDATA = %0h",trans.addr,trans.wdata);
@(posedge mem_vif.DRIVER.clk);
end
if(trans.rd_en) begin
`DRIV_IF.rd_en <= trans.rd_en;
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge mem_vif.DRIVER.clk);
trans.rdata = `DRIV_IF.rdata;
$display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
no_transactions++;
end
endtask

4. Adding a local variable to track the number of packets driven, and increment the
variable in 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.

9/17
class driver;

//used to count the number of transactions


int no_transactions;

//creating virtual interface handle


virtual mem_intf mem_vif;

//creating mailbox handle


mailbox gen2driv;

//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction

//Reset task, Reset the Interface signals to default/initial values


task reset;
wait(mem_vif.reset);
$display("--------- [DRIVER] Reset Started ---------");
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
`DRIV_IF.addr <= 0;
`DRIV_IF.wdata <= 0;
wait(!mem_vif.reset);
$display("--------- [DRIVER] Reset Ended---------");
endtask

//drive the transaction items to interface signals


task drive;
forever begin
transaction trans;
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
gen2driv.get(trans);
$display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.addr <= trans.addr;
if(trans.wr_en) begin
`DRIV_IF.wr_en <= trans.wr_en;
`DRIV_IF.wdata <= trans.wdata;
$display("\tADDR = %0h \tWDATA = %0h",trans.addr,trans.wdata);
@(posedge mem_vif.DRIVER.clk);
end
if(trans.rd_en) begin
`DRIV_IF.rd_en <= trans.rd_en;
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge mem_vif.DRIVER.clk);
trans.rdata = `DRIV_IF.rdata;
$display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");

10/17
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;

//event for synchronization between generator and test


event gen_ended;

//virtual interface
virtual mem_intf mem_vif;

2. In Construct Method, Create

Mailbox
Generator
Driver

and pass the interface handle through the new() method.

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
endfunction

11/17
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.

12/17
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
class environment;

//generator and driver instance


generator gen;
driver driv;

//mailbox handle's
mailbox gen2driv;

//event for synchronization between generator and test


event gen_ended;

//virtual interface
virtual mem_intf mem_vif;

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_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

13/17
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,

14/17
`include "environment.sv"
program test(mem_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, inorder to connect DUT and testcase


mem_intf intf(clk,reset);

3. Create Design Instance and Connect Interface signals,

15/17
//DUT instance, interface signals are connected to the DUT ports
memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);

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,

16/17
`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


mem_intf intf(clk,reset);

//Testcase instance, interface handle is passed to test as an argument


test t1(intf);

//DUT instance, interface signals are connected to the DUT ports


memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);

//enabling the wave dump


initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
endmodule

17/17
Memory Model TestBench With Monitor and

Scoreboard TestBench Architecture:

SystemVerilog TestBench

Only monitor and scoreboard are explained here, Refer to ‘Memory Model’ TestBench
Without Monitor, Agent, and Scoreboard for other components.

Monitor
Samples the interface signals and converts the signal level activity to the
transaction level
Send the sampled transaction to Scoreboard via Mailbox
Below are the steps to write a monitor

1. Writing monitor class.

class monitor;
------
endclass

2. Declare interface and mailbox, Get the interface and mailbox handle through the
constructor.

1/9
//creating virtual interface handle
virtual mem_intf mem_vif;

//creating mailbox handle


mailbox mon2scb;

//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

3. Sampling logic and sending the sampled transaction to the scoreboard

task main;
forever begin
transaction trans;
trans = new();

@(posedge mem_vif.MONITOR.clk);
wait(`MON_IF.rd_en || `MON_IF.wr_en);
trans.addr = `MON_IF.addr;
trans.wr_en = `MON_IF.wr_en;
trans.wdata = `MON_IF.wdata;
if(`MON_IF.rd_en) begin
trans.rd_en = `MON_IF.rd_en;
@(posedge mem_vif.MONITOR.clk);
@(posedge mem_vif.MONITOR.clk);
trans.rdata = `MON_IF.rdata;
end
mon2scb.put(trans);
end
endtask

4. Complete monitor code.

2/9
`define MON_IF mem_vif.MONITOR.monitor_cb
class monitor;

//creating virtual interface handle


virtual mem_intf mem_vif;

//creating mailbox handle


mailbox mon2scb;

//constructor
function new(virtual mem_intf mem_vif,mailbox mon2scb);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

//Samples the interface signal and send the sample packet to scoreboard
task main;
forever begin
transaction trans;
trans = new();

@(posedge mem_vif.MONITOR.clk);
wait(`MON_IF.rd_en || `MON_IF.wr_en);
trans.addr = `MON_IF.addr;
trans.wr_en = `MON_IF.wr_en;
trans.wdata = `MON_IF.wdata;
if(`MON_IF.rd_en) begin
trans.rd_en = `MON_IF.rd_en;
@(posedge mem_vif.MONITOR.clk);
@(posedge mem_vif.MONITOR.clk);
trans.rdata = `MON_IF.rdata;
end
mon2scb.put(trans);
end
endtask

endclass

Scoreboard
Scoreboard receives the sampled packet from monitor,

if the transaction type is “read”, compares the read data with the local memory
data if the transaction type is “write”, local memory will be written with the wdata

class scoreboard;

------

endclass

1. Declaring the mailbox and variable to keep count of transactions, connecting handle
through the constructor,

3/9
//creating mailbox handle
mailbox mon2scb;

//used to count the number of transactions


int no_transactions;

//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

2. logic to store wdata and compare rdata with stored data,

//stores wdata and compare rdata with stored data


task main;
transaction trans;
forever begin
#50;
mon2scb.get(trans);
if(trans.rd_en) begin
if(mem[trans.addr] != trans.rdata)
$error("[SCB-FAIL] Addr = %0h,\n \t Data :: Expected = %0h Actual =
%0h",trans.addr,mem[trans.addr],trans.rdata);
else
$display("[SCB-PASS] Addr = %0h,\n \t Data :: Expected = %0h Actual =
%0h",trans.addr,mem[trans.addr],trans.rdata);
end
else if(trans.wr_en)
mem[trans.addr] = trans.wdata;

no_transactions++;
end
endtask

3. Complete scoreboard code.

4/9
class scoreboard;

//creating mailbox handle


mailbox mon2scb;

//used to count the number of transactions


int no_transactions;

//array to use as local memory


bit [7:0] mem[4];

//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
foreach(mem[i]) mem[i] = 8'hFF;
endfunction

//stores wdata and compare rdata with stored data


task main;
transaction trans;
forever begin
#50;
mon2scb.get(trans);
if(trans.rd_en) begin
if(mem[trans.addr] != trans.rdata)
$error("[SCB-FAIL] Addr = %0h,\n \t Data :: Expected = %0h Actual =
%0h",trans.addr,mem[trans.addr],trans.rdata);
else
$display("[SCB-PASS] Addr = %0h,\n \t Data :: Expected = %0h Actual =
%0h",trans.addr,mem[trans.addr],trans.rdata);
end
else if(trans.wr_en)
mem[trans.addr] = trans.wdata;

no_transactions++;
end
endtask

endclass

Environment
Here only updates are mentioned. i.e adding monitor and scoreboard to the previous
example.

1. Declare the handles,

5/9
//generator and driver instance
generator gen;
driver driv;
monitor mon; //---NEW CODE---
scoreboard scb; //---NEW CODE---

//mailbox handle's
mailbox gen2driv;
mailbox mon2scb; //---NEW CODE---

//virtual interface
virtual mem_intf mem_vif;

2. In Construct Method, Create

Mailbox (mon2scb)
Monitor
Scoreboard

and pass the interface handle through the new() method.

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();
mon2scb = new();

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
mon = new(mem_vif,mon2scb);
scb = new(mon2scb);
endfunction

3. Calling monitor and scoreboard tasks,

task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();

6/9
mon.main(); //---NEW CODE---
scb.main(); //---NEW CODE---
join_any
endtask

task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions);

wait(gen.repeat_count == scb.no_transactions); //---NEW CODE---


endtask

4. Complete environment class code,

7/9
class environment;

//generator and driver instance


generator gen;
driver driv;
monitor mon;
scoreboard scb;

//mailbox handle's
mailbox gen2driv;
mailbox mon2scb;

//event for synchronization between generator and test


event gen_ended;

//virtual interface
virtual mem_intf mem_vif;

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();
mon2scb = new();

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
mon = new(mem_vif,mon2scb);
scb = new(mon2scb);
endfunction

//
task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
mon.main();
scb.main();
join_any
endtask

task post_test();
wait(gen_ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
wait(gen.repeat_count == scb.no_transactions);
endtask

//run task

8/9
task run;
pre_test();
test();
post_test();
$finish;
endtask

endclass

Edit and Execute the Memory Model TestBench code in EDA Playground.

https://www.edaplayground.com/x/3QTT

9/9

You might also like