EE5530 Lecture17 Complete SV TestBench With SCB
EE5530 Lecture17 Complete SV TestBench With SCB
SV TestBench with
Scoreboard
Memory Read
Courtesy: www.verificationguide.com
Verification Process
Before writing/creating the verification plan, need to know about design, so will go through
the design specification.
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
Memory DUT
module memory
#(
parameter ADDR_WIDTH = 2, //Memory
parameter DATA_WIDTH = 8 reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH];
)
( //Reset
input clk, always @(posedge reset)
input reset, for(int i=0;i<2**ADDR_WIDTH;i++) mem[i]=8'hFF;
Perform write to any memory location, read from the same memory location, read data should be the same
as written data
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)
Check default memory values. (before writing any locations, do read operation we should get default values
as ‘hFF)
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)
TestBench Without Monitor, Agent, and Scoreboard
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
class transaction;
endclass
Generator Class
Generator class is responsible for,
class generator;
//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
Generator Class
class generator;
//constructor
function new(mailbox gen2driv,event ended);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
this.ended = ended;
endfunction
Interface
Will group the signals, specifies the direction (Modport)
endclocking endclocking
Complete Interface
//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
For simplicity, define is used to access interface signals.
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
Drive Task (contd..)
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
class driver;
//mailbox handle's
mailbox gen2driv;
//virtual interface
virtual mem_intf mem_vif;
Environment Constructor
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();
task post_test();
task pre_test(); wait(gen_ended.triggered);
driv.reset(); wait(gen.repeat_count == driv.no_transactions);
endtask endtask
task test();
fork task run;
gen.main(); pre_test();
driv.main(); test();
join_any post_test();
endtask $finish;
endtask
`include "transaction.sv"
Complete Environment Code
`include "generator.sv"
`include "driver.sv"
class environment; //creating the mailbox
//(Same handle will be shared
//generator and driver instance //across generator and driver)
generator gen; gen2driv = new();
driver driv; task post_test();
//creating generator and driver wait(gen_ended.triggered);
//mailbox handle's gen = new(gen2driv,gen_ended); wait(gen.repeat_count ==
mailbox gen2driv; driv = new(mem_vif,gen2driv); driv.no_transactions);
endfunction endtask
//event for synchronization
//between generator and test task pre_test(); //run task
event gen_ended; driv.reset(); task run;
endtask pre_test();
//virtual interface test();
virtual mem_intf mem_vif; task test(); post_test();
fork $finish;
//constructor gen.main(); endtask
function new(virtual mem_intf mem_vif); driv.main();
//get the interface from test join_any endclass
this.mem_vif = mem_vif; endtask
Test
Test is written with program construct and is responsible for,
`include "environment.sv"
program test(mem_intf intf);
initial begin
//creating environment
env = new(intf);
//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram
`include "interface.sv"
TestBench Top
`include "random_test.sv"
endclass
TestBench With Monitor, Agent, and Scoreboard
Monitor
Monitor class is responsible for,
Samples the interface signals and converts the signal level activity to the transaction level
//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
Sampling logic
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
Complete Monitor Code task main;
forever begin
transaction trans;
trans = new();
`define MON_IF mem_vif.MONITOR.monitor_cb
class monitor;
@(posedge mem_vif.MONITOR.clk);
wait(`MON_IF.rd_en || `MON_IF.wr_en);
//creating virtual interface handle
trans.addr = `MON_IF.addr;
virtual mem_intf mem_vif;
trans.wr_en = `MON_IF.wr_en;
trans.wdata = `MON_IF.wdata;
//creating mailbox handle
if(`MON_IF.rd_en) begin
mailbox mon2scb;
trans.rd_en = `MON_IF.rd_en;
@(posedge mem_vif.MONITOR.clk);
//constructor
@(posedge mem_vif.MONITOR.clk);
function new(virtual mem_intf mem_vif,mailbox
trans.rdata = `MON_IF.rdata;
mon2scb);
end
//getting the interface
mon2scb.put(trans);
this.mem_vif = mem_vif;
end
//getting the mailbox handles from environment
endtask
this.mon2scb = mon2scb;
endfunction
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;
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
Store wdata and compare rdata with stored data,
task main;
transaction trans;
forever begin
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
task main;
transaction trans;
Complete Scoreboard forever begin
mon2scb.get(trans);
if(trans.rd_en) begin
class scoreboard; if(mem[trans.addr] != trans.rdata)
$error("[SCB-FAIL] Addr = %0h,\n \t Data ::
//creating mailbox handle Expected = %0h Actual =
mailbox mon2scb; %0h",trans.addr,mem[trans.addr],trans.rdata);
else
//used to count the number of transactions $display("[SCB-PASS] Addr = %0h,\n \t Data ::
int no_transactions; Expected = %0h Actual =
%0h",trans.addr,mem[trans.addr],trans.rdata);
//array to use as local memory end
bit [7:0] mem[4]; else if(trans.wr_en)
mem[trans.addr] = trans.wdata;
//constructor
function new(mailbox mon2scb); no_transactions++;
//getting the mailbox handles from end
environment endtask
this.mon2scb = mon2scb;
foreach(mem[i]) mem[i] = 8'hFF; endclass
endfunction
`include "transaction.sv" Complete Environment Code
`include "generator.sv"
`include "driver.sv" //creating the mailbox
class environment; //(Same handle will be shared
//across generator and driver)
//generator and driver instance gen2driv = new();
generator gen; mon2scb = new();
driver driv; task post_test();
//creating generator and driver
monitor mon; wait(gen_ended.triggered);
gen = new(gen2driv,gen_ended);
scoreboard scb; wait(gen.repeat_count ==
driv = new(mem_vif,gen2driv);
driv.no_transactions);
mon = new(mem_vif,mon2scb);
//mailbox handle's wait(gen.repeat_count ==
scb = new(mon2scb);
mailbox gen2driv; scb.no_transactions);
endfunction
mailbox mon2scb; endtask
task pre_test();
//event for synchronization //run task
driv.reset();
//between generator and test task run;
endtask
event gen_ended; pre_test();
test();
task test();
//virtual interface post_test();
fork
virtual mem_intf mem_vif; $finish;
gen.main();
endtask
driv.main();
//constructor mon.main();
function new(virtual mem_intf mem_vif); endclass
scb.main();
//get the interface from test join_any
this.mem_vif = mem_vif; endtask