Verifying The Switch RTL Core
Verifying The Switch RTL Core
Verifying The Switch RTL Core
COMPREHENSIVE GUIDE TO
VERIFYING THE SWITCH
RTL CORE
STEP-BY-
STEP
TUTORIAL
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_01_INTRODUCTION.html
INTRODUCTION
In this tutorial, we will verify the Switch RTL core. Following are the steps we follow to
verify the Switch RTL core.
3) Building the Verification Environment. We will build the Environment in Multiple phases,
so it will be easy for you to lean step by step.
Phase 1) We will develop the testcase and interfaces, and integrate them in these
with the DUT in top module.
Phase 3) We will develop reset and configuration methods in Environment class. Then
using these methods, we will reset the DUT and configure the port address.
Phase 4) We will develop a packet class based on the stimulus plan. We will also
write a small code to test the packet class implementation.
Phase 5) We will develop a driver class. Packets are generated and sent to dut using
driver.
Phase 6) We will develop receiver class. Receiver collects the packets coming from
the output port of the DUT.
Phase 7) We will develop scoreboard class which does the comparison of the
expected packet with the actual packet received from the DUT.
Phase 9) In this phase , we will write testcases and analyze the coverage report.
1/1
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_02_SPECIFICATION.html
SPECIFICATION
Switch Specification:
This is a simple switch. Switch is a packet based protocol. Switch drives the incoming
packet which comes from the input port to output ports based on the address contained in
the packet.
The switch has a one input port from which the packet enters. It has four output ports
where the packet is driven out.
Packet Format:
Packet contains 3 parts. They are Header, data and frame check sequence.
Packet width is 8 bits and the length of the packet can be between 4 bytes to
259 bytes.
Packet Header:
DA: Destination address of the packet is of 8 bits. The switch drives the packet to
respective ports based on this destination address of the packets. Each output port has 8-
bit unique port address. If the destination address of the packet matches the port
address, then switch drives the packet to the output port.
1/4
SA: Source address of the packet from where it originate. It is 8 bits.
Length: Length of the data is of 8 bits and from 0 to 255. Length is measured in terms
of bytes.
If Length = 0, it means data length is 0 bytes
This field contains the security check of the packet. It is calculated over the header and
data.
Configuration:
Switch has four output ports. These output ports address have to be configured to a
unique address. Switch matches the DA field of the packet with this configured port
address and sends the packet on to that port. Switch contains a memory. This memory
has 4 locations, each can store 8 bits. To configure the switch port address, memory write
operation has to be done using memory interface. Memory address (0,1,2,3) contains the
address of port(0,1,2,3) respectively.
Interface Specification:
The Switch has one input Interface, from where the packet enters and 4 output interfaces
from where the packet comes out and one memory interface, through the port address
can be configured. Switch also has a clock and asynchronous reset signal.
Memory Interface:
2/4
Through memory interfaced output port address are configured. It accepts 8 bit data to be
written to memory. It has 8 bit address inputs. Address 0,1,2,3 contains the address of the
port 0,1,2,3 respectively.
input mem_en;
input mem_rd_wr;
input[1:0] mem_add;
input[7:0] mem_data;
All the signals are active high and are synchronous to the positive edge of clock signal.
Input Port
All the signals are active high and are synchronous to the positive edge of clock signal.
input[7:0]data;
input data_status;
3. After sending all the data bytes, deassert the data_status signal.
4. There should be at least 3 clock cycles difference between packets.
Output Port
3/4
Switch sends the packets out using the output ports. There are 4 ports, each having data,
ready and read signals. All the signals are active high and are synchronous to the positive
edge of clock signal.
Signal list is
output[7:0] port0;
output[7:0] port1;
output[7:0] port2;
output[7:0] port3;
output ready_0;
output ready_1;
output ready_2;
output ready_3;
input read_0;
input read_1;
input read_2;
input read_3;
When the data is ready to be sent out from the port, switch asserts ready_* signal high
indicating that data is ready to be sent.
If the read_* signal is asserted, when
ready_* is high, then the data comes out of the
port_* signal after one clock cycle.
RTL code is attached with the tar files. From the Phase 1, you can download the tar files.
4/4
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_03_VERIFICATION_PLAN.html
VERIFICATION PLAN
Overview
This Document describes the Verification Plan for Switch. The Verification Plan is based
on System Verilog Hardware Verification Language. The methodology used for
Verification is Constraint random coverage driven verification.
Feature Extraction
ID: Configuration
2)
ID: Packet DA
field of packet should be any of the port address. All the 4 port address
Description: DA
should be used.
3)
ID : Packet payload
Description: Length
can be from 0 to 255. Send packets with all the lengths.
4)
ID: Length
Description:
Length field
contains length of the payload.
Send Packet with correct length field and incorrect length fields.
5)
ID: FCS
Description:
Good FCS:
Send packet with good FCS.
1/2
Coverage Plan
Verification Environment
Report a Bug or Comment on This section - Your input is what keeps Testbench.in
improving with time!
2/2
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_04_PHASE_1_TOP.html
PHASE 1 TOP
In phase 1,
1) We will write SystemVerilog Interfaces for input port, output port and memory port.
2) We will write Top module where testcase and DUT instances are done.
`ifndefGUARD_*
`endifGUARD_*
Interfaces
All the signals are synchronized to clock except reset in clocking block.
`ifndefGUARD_INTERFACE
`defineGUARD_INTERFACE
//////////////////////////////////////////
logic[7:0] mem_data;
logic[1:0] mem_add;
logic mem_en;
logic mem_rd_wr;
defaultinput#1output#1;
output mem_data;
output mem_add;
output mem_en;
output mem_rd_wr;
endclocking
1/7
endinterface
////////////////////////////////////////////
////////////////////////////////////////////
logic[7:0] data_in;
logic reset;
output data_status;
output data_in;
endclocking
endinterface
/////////////////////////////////////////////////
/////////////////////////////////////////////////
logic[7:0] data_out;
logic ready;
logic read;
defaultinput#1output#1;
input data_out;
input ready;
output read;
endclocking
endinterface
//////////////////////////////////////////////////
`endif
Testcase
2/7
Testcase is a program block which provides an entry point for the test and creates a
scope that encapsulates program-wide data. Currently this is an empty testcase which
just ends the simulation after 100 time units. Program block contains all the above
declared interfaces as arguments. This testcase has initial and final blocks.
`ifndefGUARD_TESTCASE
`defineGUARD_TESTCASE
initial
begin
#1000;
end
final
endprogram
`endif
Top Module
The modules that are included in the source text but are not instantiated are called top
modules. This module is the highest scope of modules. Generally this module is named
as "top" and referenced as "top module". Module name can be anything.
bit Clock;
initial
mem_interface mem_intf(Clock);
input_interface input_intf(Clock);
output_interface output_intf[4](Clock);
3/7
5)Do the instance of testcase and pass all the above declared interfaces.
testcase TC(mem_intf,input_intf,output_intf);
switch DUT(.
7)Connect all the interfaces and DUT. The design which we have taken is in verilog. So
Verilog DUT instance is connected signal by signal.
switch DUT(.clk(Clock),
.reset(input_intf.reset),
.data_status(input_intf.data_status),
.data(input_intf.data_in),
.port0(output_intf[0].data_out),
.port1(output_intf[1].data_out),
.port2(output_intf[2].data_out),
.port3(output_intf[3].data_out),
.ready_0(output_intf[0].ready),
.ready_1(output_intf[1].ready),
.ready_2(output_intf[2].ready),
.ready_3(output_intf[3].ready),
.read_0(output_intf[0].read),
.read_1(output_intf[1].read),
.read_2(output_intf[2].read),
.read_3(output_intf[3].read),
.mem_en(mem_intf.mem_en),
.mem_rd_wr(mem_intf.mem_rd_wr),
.mem_add(mem_intf.mem_add),
.mem_data(mem_intf.mem_data));
4/7
Top Module Source Code:
`ifndefGUARD_TOP
`defineGUARD_TOP
module top();
/////////////////////////////////////////////////////
// Clock Declaration and Generation
//
/////////////////////////////////////////////////////
bit Clock;
initial
/////////////////////////////////////////////////////
// Memory interface instance //
/////////////////////////////////////////////////////
mem_interface mem_intf(Clock);
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
input_interface input_intf(Clock);
5/7
/////////////////////////////////////////////////////
// output interface instance //
/////////////////////////////////////////////////////
output_interface output_intf[4](Clock);
/////////////////////////////////////////////////////
// Program block Testcase instance
//
/////////////////////////////////////////////////////
testcase TC(mem_intf,input_intf,output_intf);
/////////////////////////////////////////////////////
switch DUT(.clk(Clock),
.reset(input_intf.reset),
.data_status(input_intf.data_status),
.data(input_intf.data_in),
.port0(output_intf[0].data_out),
.port1(output_intf[1].data_out),
.port2(output_intf[2].data_out),
.port3(output_intf[3].data_out),
.ready_0(output_intf[0].ready),
.ready_1(output_intf[1].ready),
.ready_2(output_intf[2].ready),
.ready_3(output_intf[3].ready),
.read_0(output_intf[0].read),
.read_1(output_intf[1].read),
.read_2(output_intf[2].read),
.read_3(output_intf[3].read),
.mem_en(mem_intf.mem_en),
.mem_rd_wr(mem_intf.mem_rd_wr),
.mem_add(mem_intf.mem_add),
.mem_data(mem_intf.mem_data));
endmodule
`endif
switch_1.tar
6/7
(S)Log file after simulation:
7/7
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_05_PHASE_2_ENVIRONMENT.html
PHASE 2 ENVIRONMENT
Environment class.
Defining required methods for execution. Currently these methods will not be
implemented in this phase.
We will write a testcase using the above define environment class in testcase.sv file.
Environment Class:
The class is a base class used to implement verification environments. Testcase contains
the instance of the environment class and has access to all the public declaration of
environment class.
All methods are declared as virtual methods. In environment class, we will formalize the
simulation steps using virtual methods. The methods are used to control the execution of
the simulation.
Following are the methods which are going to be defined in environment class.
1) new() : In constructor method, we will connect the virtual interfaces which are passed
as argument to the virtual interfaces to those which are declared in environment class.
2) build(): In this method , all the objects like driver, monitor etc are constructed. Currently
this method is empty as we did not develop any other component.
4) cfg_dut(): In this method, we will configure the DUT output port address.
5) start(): in this method, we will call the methods which are declared in the other
components like driver and monitor.
6) wait_for_end(): this method is used to wait for the end of the simulation. Waits until all
the required operations in other components are done.
7) report(): This method is used to print the TestPass and TestFail status of the
simulation, based on the error count..
1/6
8) run(): This method calls all the above declared methods in a sequence order. The
testcase calls this method, to start the simulation.
We are not implementing build(), reset(), cfg_dut() , strat() and report() methods in this
phase.
Connecting the virtual interfaces of Environment class to the physical interfaces of top
module.
Constructor method should be declared with virtual interface as arguments, so that when
the object is created in testcase, new() method can pass the interfaces in to environment
class where they are assigned to the local virtual interface handle. With this, the
Environment class virtual interfaces are pointed to the physical interfaces which are
declared in the top module.
2/6
In constructor methods, the interfaces which are arguments are connected to the virtual
interfaces of environment class.
this.mem_intf = mem_intf_new ;
this.input_intf = input_intf_new ;
this.output_intf = output_intf_new ;
Run :
The run() method is called from the testcase to start the simulation. run() method calls all
the methods which are defined in the Environment class.
task run();
build();
reset();
cfg_dut();
start();
wait_for_end();
report();
endtask: run
`ifndefGUARD_ENV
`defineGUARD_ENV
class Environment ;
3/6
virtual mem_interface.MEM mem_intf ;
virtual input_interface.IP input_intf ;
this.mem_intf = mem_intf_new ;
this.input_intf = input_intf_new ;
this.output_intf = output_intf_new
;
endfunction:new
functionvoid build();
endfunction:build
task reset();
: Environment : start of reset() method",$time);
$display(" %0d
task cfg_dut();
endtask: cfg_dut
taskstart();
endtask:start
task wait_for_end();
endtask: wait_for_end
task run();
reset();
cfg_dut();
start();
wait_for_end();
4/6
report();
$display(" %0d : Environment : end of run() method",$time);
endtask: run
task report();
endtask: report
endclass
`endif
We will create a file Global.sv for global requirement. In this file, define all the port
address as macros in this file. Define a variable error as integer to keep track the number
of errors occurred during the simulation.
`ifndefGUARD_GLOBALS
`defineGUARD_GLOBALS
`defineP08'h00
`defineP18'h11
`defineP28'h22
`defineP38'h33
`endif
Now we will update the testcase. Take an instance of the Environment class and call the
run method of the Environment class.
`ifndef GUARD_TESTCASE
`define GUARD_TESTCASE
Environment env;
initial
begin
env =new(mem_intf,input_intf,output_intf);
env.run();
#1000;
end
5/6
final
$display(" ******************** End of testcase *****************");
endprogram
`endif
switch_2.tar
6/6
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_06_PHASE_3_RESET.html
PHASE 3 RESET
The Environment class has reset() method which contains the logic to reset the DUT and
cfg_dut() method which contains the logic to configure the DUT port address.
NOTE: Clocking block signals can be driven only using a non-blocking assignment.
mem_intf.cb.mem_data <=0;
mem_intf.cb.mem_add <=0;
mem_intf.cb.mem_en <=0;
mem_intf.cb.mem_rd_wr <=0;
input_intf.cb.data_in <=0;
input_intf.cb.data_status <=0;
output_intf[0].cb.read <=0;
output_intf[1].cb.read <=0;
output_intf[2].cb.read <=0;
output_intf[3].cb.read <=0;
input_intf.reset <=1;
repeat(4)@ input_intf.clock;
input_intf.reset <=0;
1/3
3) Updated the cfg_dut method.
task cfg_dut();
mem_intf.cb.mem_en <=1;
@(posedge mem_intf.clock);
mem_intf.cb.mem_rd_wr <=1;
@(posedge mem_intf.clock);
mem_intf.cb.mem_add <=8'h0;
mem_intf.cb.mem_data <=`P0;
@(posedge mem_intf.clock);
mem_intf.cb.mem_add <=8'h1;
mem_intf.cb.mem_data <=`P1;
@(posedge mem_intf.clock);
mem_intf.cb.mem_add <=8'h2;
mem_intf.cb.mem_data <=`P2;
@(posedge mem_intf.clock);
mem_intf.cb.mem_add <=8'h3;
mem_intf.cb.mem_data <=`P3;
@(posedge mem_intf.clock);
mem_intf.cb.mem_en <=0;
mem_intf.cb.mem_rd_wr <=0;
mem_intf.cb.mem_add <=0;
mem_intf.cb.mem_data <=0;
endtask: cfg_dut
task wait_for_end();
repeat(10000)@(input_intf.clock);
2/3
switch_3.tar
3/3
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_07_PHASE_4_PACKET.html
PHASE 4 PACKET
In this Phase, We will define a packet and then test it whether it is generating as
expected.
Packet is modeled using class. Packet class should be able to generate all possible
packet types randomly. Packet class should also implement required methods like
packing(), unpacking(), compare() and display() methods.
We will write the packet class in packet.sv file. Packet class variables and constraints
have been derived from stimulus generation plan.
1) Declare FCS types as enumerated data types. Name members as GOOD_FCS and
BAD_FCS.
typedefenum{GOOD_FCS,BAD_FCS} fcs_kind_t;
typedefenum{GOOD_LENGTH,BAD_LENGTH} length_kind_t;
4) Declare the packet field as rand. All fields are bit data types. All fields are 8 bit packet
array. Declare the payload as dynamic array.
randbit[7:0] length;
randbit[7:0] da;
randbit[7:0] sa;
randbyte fcs;
1/10
constraint address_c { da inside{`P0,`P1,`P2,`P3};}
7) Constrain the payload length to the length field based on the length type.
constraint length_kind_c {
Use solve before to direct the randomization to generate first the payload dynamic array
size and then randomize length field.
8) Constrain the FCS field initial value based on the fcs kind field.
constraint fcs_kind_c {
functionbyte cal_fcs;
integer i;
byte result ;
result =0;
for(i =0;i<data.size;i++)
return result;
endfunction: cal_fcs
Display method displays the current value of the packet fields to standard output.
virtualfunctionvoid display();
$display("\n---------------------- PACKET KIND ------------------------- ");
$display(" 0 : %h ",da);
$display(" 1 : %h ",sa);
2/10
$display(" 2 : %h ",length);
foreach(data[i])
$display("----------------------------------------------------------- \n");
endfunction: display
Packing is commonly used to convert the high level data to low level data that can be
applied to DUT. In packet class various fields are generated. Required fields are
concatenated to form a stream of bytes which can be driven conveniently to DUT
interface by the driver.
bytes =new[data.size+4];
bytes[0]= da;
bytes[1]= sa;
bytes[2]= length;
foreach(data[i])
bytes[3+ i]=data[i];
bytes[data.size()+3]=
cal_fcs;
byte_pack = bytes.size;
endfunction: byte_pack
3/10
The unpack() method does exactly the opposite of pack method. Unpacking is commonly
used to convert a data stream coming from DUT to high level data packet object.
this.sa = bytes[1];
this.length = bytes[2];
this.fcs = bytes[bytes.size-1];
this.data=new[bytes.size-4];
foreach(data[i])
this.fcs =0;
if(bytes[bytes.size-1]!= cal_fcs)
this.fcs =1;
endfunction: byte_unpack
Compares the current value of the object instance with the current value of the specified
object instance.
virtualfunctionbitcompare(packet pkt);
compare=1;
if(pkt ==null)
begin
end
else
begin
if(pkt.da !==this.da)
begin
4/10
compare=0;
end
if(pkt.sa !==this.sa)
begin
compare=0;
end
if(pkt.length !==this.length)
begin
compare=0;
end
foreach(this.data[i])
if(pkt.data[i]!==this.data[i])
begin
compare=0;
end
if(pkt.fcs !==this.fcs)
begin
$display(" ** ERROR **: pkt : fcs field did not match %h %h",pkt.fcs ,this.fcs);
compare=0;
end
end
endfunction:compare
`ifndefGUARD_PACKET
`defineGUARD_PACKET
typedefenum{GOOD_LENGTH,BAD_LENGTH} length_kind_t;
class packet;
randbit[7:0] length;
randbit[7:0] da;
randbit[7:0] sa;
randbyte fcs;
5/10
constraint address_c { da inside{`P0,`P1,`P2,`P3};}
constraint length_kind_c {
constraint fcs_kind_c {
integer i;
byte result ;
result =0;
for(i =0;i<data.size;i++)
result = result ^data[i];
return result;
endfunction: cal_fcs
$display(" 0 : %h ",da);
$display(" 1 : %h ",sa);
$display(" 2 : %h ",length);
foreach(data[i])
$display("----------------------------------------------------------- \n");
endfunction: display
bytes =new[data.size+4];
bytes[0]= da;
6/10
bytes[1]= sa;
bytes[2]= length;
foreach(data[i])
bytes[3+ i]=data[i];
bytes[data.size()+3]= cal_fcs;
byte_pack = bytes.size;
endfunction: byte_pack
this.da = bytes[0];
this.sa = bytes[1];
this.length = bytes[2];
this.fcs = bytes[bytes.size-1];
this.data=new[bytes.size-4];
foreach(data[i])
if(bytes[bytes.size-1]!= cal_fcs)
this.fcs =1;
endfunction:
byte_unpack
compare=1;
if(pkt ==null)
begin
end
else
begin
if(pkt.da !==this.da)
begin
end
if(pkt.sa !==this.sa)
begin
compare=0;
end
7/10
if(pkt.length !==this.length)
begin
end
foreach(this.data[i])
if(pkt.data[i]!==this.data[i])
begin
compare=0;
end
if(pkt.fcs !==this.fcs)
begin
$display(" ** ERROR **: pkt : fcs field did not match %h %h",pkt.fcs ,this.fcs);
compare=0;
end
end
endfunction:compare
endclass
Now we will write a small program to test our packet implantation. This program block is
not used to verify the DUT.
Write a simple program block and do the instance of packet class. Randomize the packet
and call the display method to analyze the generation. Then pack the packet in to bytes
and then unpack bytes and then call compare method to check all the methods.
program test;
initial
repeat(10)
if(pkt1.randomize)
begin
void'(pkt1.byte_pack(bytes));
pkt2 =new();
pkt2.byte_unpack(bytes);
if(pkt2.compare(pkt1))
8/10
$display(" Packing,Unpacking and compare worked");
else
end
else
endprogram
switch_4.tar
Randomization Sucessesfull.
length_kind : GOOD_LENGTH
-------- PACKET ----------
0 : 00
1 : f7
2 : be
3 : a6 4 : 1b 5 : b5 6 : fa 7 : 4e 8 : 15 9 : 7d 10 : 72 11 : 96 12 : 31 13 : c4 14 : aa 15 :
c4 16 : cf 17 : 4f 18 : f4 19 : 17 20 : 88 21 : f1 22 : 2c 23 : ce 24 : 5 25 : cb 26 : 8c 27 :
1a 28 : 37 29 : 60 30 : 5f 31 : 7a 32 : a2 33 : f0 34 : c9 35 : dc 36 : 41 37 : 3f 38 : 12 39
: f4 40 : df 41 : c5 42 : d7 43 : 94 44 : 88 45 : 1 46 : 31 47 : 29 48 : d6 49 : f4 50 : d9 51
: 4f 52 : 0 53 : dd 54 : d2 55 : a6 56 : 59 57 : 43 58 : 45 59 : f2 60 : a2 61 : a1 62 : fd 63
: ea 64 : c1 65 : 20 66 : c7 67 : 20 68 : e1 69 : 97 70 : c6 71 : cf 72 : cd 73 : 17 74 : 99
75 : 49 76 : b8 77 : 1c 78 : df 79 : e6 80 : 1a 81 : ce 82 : 8c 83 : ec 84 : b6 85 : bb 86 :
a5 87 : 17 88 : cb 89 : 32 90 : e1 91 : 83 92 : 96 93 : e 94 : ee 95 : 57 96 : 33 97 : cd 98
: 62 99 : 88 100 : 7b 101 : e6 102 : 41 103 : ad 104 : 26 105 : ee 106 : 9c 107 : 95 108 :
a7 109 : b8 110 : 83 111 : f 112 : ca 113 : ec 114 : b5 115 : 8d 116 : d8 117 : 2f 118 : 6f
119 : ea 120 : 4c 121 : 35 122 : 41 123 : f2 124 : 4e 125 : 89 126 : d8 127 : 78 128 : f1
129 : d 130 : d6 131 : d5 132 : 8 133 : c 134 : de 135 : a9 136 : 1d 137 : a0 138 : ae
139 : 99 140 : f5 141 : 53 142 : d8 143 : 7a 144 : 4c 145 : d4 146 : b8 147 : 54 148 : b7
149 : c3 150 : c9 151 : 7b 152 : a3 153 : 71 154 : 2b 155 : b4 156 : 50 157 : 54 158 : 22
159 : 95 160 : df 161 : 17 162 : c9 163 : 41 164 : 80 165 : 2b 166 : f0 167 : ba 168 : 4a
169 : a9 170 : 7f 171 : 13 172 : 1e 173 : 12 174 : a8 175 : 2 176 : 3 177 : 3d 178 : 71
179 : e6 180 : 96 181 : 89 182 : c6 183 : 46 184 : d6 185 : 1b 186 : 5f 187 : 20 188 : a0
9/10
189 : a3 190 : 49 191 : 79 192 : 9
193 : 53
-----------------------------------------------------------
Randomization Sucessesfull.
..............
..............
..............
10/10
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_08_PHASE_5_DRIVER.html
PHASE 5 DRIVER
In phase 5 we will write a driver and then instantiate the driver in environment and send
packet in to DUT. Driver class is defined in Driver.sv file.
Driver is class which generates the packets and then drives it to the DUT input interface
and pushes the packet in to mailbox.
1) Declare a packet.
packet gpkt;
2) Declare a virtual input_interface of the switch. We will connect this to the Physical
interface of the top module same as what we did in environment class.
3) Define a mailbox "drvr2sb" which is used to send the packets to the score board.
mailbox drvr2sb;
4) Define new constructor with arguments, virtual input interface and a mail box which is
used to send packets from the driver to scoreboard.
this.input_intf = input_intf_new ;
if(drvr2sb ==null)
1/9
begin
$display(" **ERROR**: drvr2sb is null");
$finish;
end
else
this.drvr2sb = drvr2sb;
gpkt =new();
repeat($root.num_of_pkts)
if( pkt.randomize())
begin
...........
else
begin
............
...........
pkt.display();
length = pkt.byte_pack(bytes);
Then send the packet byte in to the switch by asserting data_status of the input interface
signal and driving the data bytes on to the data_in signal.
foreach(bytes[i])
begin
@(posedge input_intf.clock);
input_intf.cb.data_status <=1;
end
2/9
After driving all the data bytes, deassert data_status signal of the input interface.
@(posedge input_intf.clock);
input_intf.cb.data_status <=0;
input_intf.cb.data_in <=0;
drvr2sb.put(pkt);
If randomization fails, increment the error counter which is defined in Globals.sv file
$root.error++;
`ifndefGUARD_DRIVER
`defineGUARD_DRIVER
class Driver;
mailbox drvr2sb;
packet gpkt;
this.input_intf = input_intf_new ;
if(drvr2sb ==null)
begin
end
else
this.drvr2sb = drvr2sb;
gpkt =new();
endfunction:new
taskstart();
packet pkt;
int length;
logic[7:0] bytes[];
repeat($root.num_of_pkts)
begin
repeat(3)@(posedge input_intf.clock);
3/9
if( pkt.randomize())
begin
pkt.display();
///// assert the data_status signal and send the packed bytes //////
foreach(bytes[i])
begin
@(posedge input_intf.clock);
input_intf.cb.data_status <=1;
@(posedge input_intf.clock);
input_intf.cb.data_status <=0;
input_intf.cb.data_in <=0;
$display(" %0d : Driver : Finished Driving the packet with length %0d",$time,length);
end
else
begin
end
end
endtask:start
endclass
`endif
Now we will take the instance of the driver in the environment class.
4/9
1) Declare a mailbox "drvr2sb" which will be used to connect the scoreboard and driver.
mailbox drvr2sb;
Driver drvr;
drvr2sb =new();
4) In build method, construct the driver object. Pass the input_intf and "drvr2sb" mail box.
drvr=new(input_intf,drvr2sb);
5) To start sending the packets to the DUT, call the start method of "drvr" in the start
method of Environment class.
drvr.start();
`ifndef GUARD_ENV
`define GUARD_ENV
class Environment ;
5/9
virtual mem_interface.MEM mem_intf ;
virtual input_interface.IP input_intf ;
Driver drvr;
mailbox drvr2sb;
this.mem_intf = mem_intf_new ;
this.input_intf = input_intf_new ;
this.output_intf = output_intf_new ;
endfunction : new
drvr2sb =new();
drvr=new(input_intf,drvr2sb);
task reset();
mem_intf.cb.mem_data <= 0;
mem_intf.cb.mem_add <= 0;
mem_intf.cb.mem_en <= 0;
mem_intf.cb.mem_rd_wr <= 0;
input_intf.cb.data_in <= 0;
input_intf.cb.data_status <= 0;
output_intf[0].cb.read <= 0;
output_intf[1].cb.read <= 0;
output_intf[2].cb.read <= 0;
output_intf[3].cb.read <= 0;
6/9
endtask : reset
task cfg_dut();
mem_intf.cb.mem_en <= 1;
@(posedge mem_intf.clock);
mem_intf.cb.mem_rd_wr <= 1;
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
mem_intf.cb.mem_add <= 8'h1;
@(posedge mem_intf.clock);
mem_intf.cb.mem_add <= 8'h2;
@(posedge mem_intf.clock);
mem_intf.cb.mem_add <= 8'h3;
@(posedge mem_intf.clock);
mem_intf.cb.mem_en <=0;
mem_intf.cb.mem_rd_wr <= 0;
mem_intf.cb.mem_add <= 0;
mem_intf.cb.mem_data <= 0;
endtask :cfg_dut
task start();
drvr.start();
7/9
$display(" %0d : Environment : end of start() method",$time);
endtask : start
task wait_for_end();
repeat(10000) @(input_intf.clock);
$display(" %0d : Environment : end
of wait_for_end() method",$time);
endtask : wait_for_end
task run();
$display("
%0d : Environment : start of run() method",$time);
build();
reset();
cfg_dut();
start();
wait_for_end();
report();
endtask : run
task report();
endtask : report
endclass
`endif
switch_5.tar
8/9
150 : Environment : end of cfg_dut() method
150 : Environment : start of start() method
fcs_kind : BAD_FCS
length_kind : GOOD_LENGTH
0 : 22
1 : 11
2 : 2d
3 : 63
4 : 2a 5 : 2e 6 : c 7 : a 8 : 14 9 : c1 10 : 14 11 : 8f 12 : 54 13 : 5d 14 : da 15 : 22
16 : 2c 17 : ac 18 : 1c 19 : 48 20 : 3c 21 : 7e 22 : f3 23 : ed 24 : 24 25 : d1 26 : 3e 27 :
38 28 : aa 29 : 54 30 : 19 31 : 89 32 : aa 33 : cf 34 : 67 35 : 19 36 : 9a 37 : 1d 38 : 96 39
: 8 40 : 15 41 : 66 42 : 55 43 : b 44 : 70 45 : 35 46 : fc 47 : 8f
48 : cd
-----------------------------------------------------------
..................
..................
..................
9/9
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_09_PHASE_6_RECEIVER.html
PHASE 6 RECEIVER
In this phase, we will write a receiver and use the receiver in environment class to collect
the packets coming from the switch output_interface.
Receiver collects the data bytes from the interface signal. And then unpacks the bytes in
to packet and pushes it into mailbox.
1) Declare a virtual output_interface. We will connect this to the Physical interface of the
top module, same as what we did in environment class.
2) Declare a mailbox "rcvr2sb" which is used to send the packets to the score board
mailbox rcvr2sb;
3) Define new constructor with arguments, virtual input interface and a mail box which is
used to send packets from the receiver to scoreboard.
if(rcvr2sb ==null)
begin
1/8
$finish;
end
else
this.rcvr2sb = rcvr2sb;
endfunction:new
wait(output_intf.cb.ready)
If the ready signal is asserted, then request the DUT to send the data out from the
data_out signal by asserting the read signal. When the data to be sent is finished by the
DUT, it will deassert the ready signal. Once the ready signal is deasserted, stop collecting
the data bytes and deasseart the read signal.
output_intf.cb.read <=1;
repeat(2)@(posedge output_intf.clock);
while(output_intf.cb.ready)
begin
bytes =new[bytes.size+1](bytes);
bytes[bytes.size-1]= output_intf.cb.data_out;
@(posedge output_intf.clock);
end
output_intf.cb.read <=0;
@(posedge output_intf.clock);
pkt =new();
Then call the unpack method of the packet to unpacked the bytes and then display the
packet content.
pkt.byte_unpack(bytes);
pkt.display();
rcvr2sb.put(pkt);
bytes.delete();
2/8
Receiver Class Source Code:
`ifndefGUARD_RECEIVER
`defineGUARD_RECEIVER
class Receiver;
if(rcvr2sb ==null)
begin
$finish;
end
else
this.rcvr2sb = rcvr2sb;
endfunction:new
taskstart();
logic[7:0] bytes[];
packet pkt;
forever
begin
repeat(2)@(posedge output_intf.clock);
wait(output_intf.cb.ready)
output_intf.cb.read <=1;
repeat(2)@(posedge output_intf.clock);
while(output_intf.cb.ready)
begin
bytes =new[bytes.size+1](bytes);
bytes[bytes.size-1]= output_intf.cb.data_out;
@(posedge output_intf.clock);
end
output_intf.cb.read <=0;
@(posedge output_intf.clock);
pkt =new();
pkt.byte_unpack(bytes);
pkt.display();
rcvr2sb.put(pkt);
3/8
bytes.delete();
end
endtask:start
endclass
`endif
Now we will take the instance of the receiver in the environment class.
1) Declare a mailbox "rcvr2sb" which will be used to connect the scoreboard and receiver.
mailbox rcvr2sb;
Receiver rcvr[4];
rcvr2sb =new();
4) In build method, construct the receiver object. Pass the output_intf and "rcvr2sb" mail
box. There are 4 output interfaces and receiver objects. We will connect one receiver for
one output interface.
foreach(rcvr[i])
rcvr[i]=new(output_intf[i],rcvr2sb);
4/8
5) To start collecting the packets from the DUT, call the "start" method of "rcvr" in the
"start" method of Environment class.
taskstart();
$display(" %0d
: Environment : start of start() method",$time);
fork
drvr.start();
rcvr[0].start();
rcvr[1].start();
rcvr[2].start();
rcvr[3].start();
join_any
$display("
%0d : Environment : end of start() method",$time);
endtask:start
`ifndef GUARD_ENV
`define GUARD_ENV
class Environment ;
Driver drvr;
Receiver rcvr[4];
mailbox drvr2sb;
mailbox rcvr2sb;
this.mem_intf = mem_intf_new ;
this.input_intf = input_intf_new ;
this.output_intf = output_intf_new ;
endfunction : new
5/8
rcvr2sb =new();
drvr= new(input_intf,drvr2sb);
foreach(rcvr[i])
rcvr[i]=new(output_intf[i],rcvr2sb);
task reset();
mem_intf.cb.mem_data <= 0;
mem_intf.cb.mem_add <= 0;
mem_intf.cb.mem_en <= 0;
mem_intf.cb.mem_rd_wr <= 0;
input_intf.cb.data_in <= 0;
input_intf.cb.data_status <=
0;
output_intf[0].cb.read <= 0;
output_intf[1].cb.read <= 0;
output_intf[2].cb.read <= 0;
output_intf[3].cb.read <= 0;
task cfg_dut();
$display(" %0d
: Environment : start of cfg_dut() method",$time);
mem_intf.cb.mem_en <= 1;
@(posedge mem_intf.clock);
mem_intf.cb.mem_rd_wr <= 1;
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
6/8
mem_intf.cb.mem_data <= `P1;
$display(" %0d : Environment : Port 1 Address %h ",$time,`P1);
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
mem_intf.cb.mem_en <=0;
mem_intf.cb.mem_rd_wr <= 0;
mem_intf.cb.mem_add <= 0;
mem_intf.cb.mem_data <= 0;
endtask :cfg_dut
task start();
$display(" %0d
: Environment : start of start() method",$time);
fork
drvr.start();
rcvr[0].start();
rcvr[1].start();
rcvr[2].start();
rcvr[3].start();
join_any
%0d : Environment : end of start() method",$time);
$display("
endtask : start
task wait_for_end();
repeat(10000) @(input_intf.clock);
$display(" %0d : Environment : end
of wait_for_end() method",$time);
endtask : wait_for_end
task run();
$display("
%0d : Environment : start of run() method",$time);
build();
7/8
reset();
cfg_dut();
start();
wait_for_end();
report();
$display("
%0d : Environment : end of run() method",$time);
endtask : run
task report();
endtask: report
endclass
`endif
switch_6.tar
8/8
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_10_PHASE_7_SCOREBOARD.html
PHASE 7 SCOREBOARD
Scoreboard has 2 mailboxes. One is used to for getting the packets from the driver and
other from the receiver. Then the packets are compared and if they don't match, then
error is asserted.
mailbox drvr2sb;
mailbox rcvr2sb;
3) Connect the mailboxes of the constructor to the mail boxes of the scoreboard.
this.drvr2sb = drvr2sb;
this.rcvr2sb = rcvr2sb;
1/7
rcvr2sb.get(pkt_rcv);
$display(" %0d : Scorebooard : Scoreboard received a packet from receiver ",$time);
drvr2sb.get(pkt_exp);
Compare both packets and increment an error counter if they are not equal.
if(pkt_rcv.compare(pkt_exp))
$display(" %0d : Scoreboardd
:Packet Matched ",$time);
else
$root.error++;
`ifndefGUARD_SCOREBOARD
`defineGUARD_SCOREBOARD
class Scoreboard;
mailbox drvr2sb;
mailbox rcvr2sb;
this.drvr2sb = drvr2sb;
this.rcvr2sb = rcvr2sb;
endfunction:new
taskstart();
packet pkt_rcv,pkt_exp;
forever
begin
rcvr2sb.get(pkt_rcv);
drvr2sb.get(pkt_exp);
if(pkt_rcv.compare(pkt_exp))
$root.error++;
end
endtask:start
endclass
`endif
Now we will see how to connect the scoreboard in the Environment class.
2/7
1) Declare a scoreboard.
Scoreboard sb;
2) Construct the scoreboard in the build method. Pass the drvr2sb and rcvr2sb mailboxes
to the score board constructor.
sb =new(drvr2sb,rcvr2sb);
sb.start();
task report();
$display("\n\n*************************************************");
if(0==$root.error)
$display("********
TEST PASSED *********");
else
$display("*************************************************\n\n");
endtask: report
3/7
Source Code Of The Environment Class:
`ifndef GUARD_ENV
`define GUARD_ENV
class Environment ;
Driver drvr;
Receiver rcvr[4];
Scoreboard sb;
mailbox drvr2sb ;
mailbox rcvr2sb ;
this.mem_intf = mem_intf_new ;
this.input_intf = input_intf_new ;
this.output_intf = output_intf_new
;
endfunction : new
drvr2sb = new();
rcvr2sb = new();
sb =new(drvr2sb,rcvr2sb);
drvr= new(input_intf,drvr2sb);
foreach(rcvr[i])
rcvr[i]= new(output_intf[i],rcvr2sb);
$display(" %0d : Environment : end
of build() method",$time);
endfunction : build
task reset();
mem_intf.cb.mem_data <= 0;
mem_intf.cb.mem_add <= 0;
4/7
mem_intf.cb.mem_en <= 0;
mem_intf.cb.mem_rd_wr <= 0;
input_intf.cb.data_in <= 0;
input_intf.cb.data_status <= 0;
output_intf[0].cb.read <= 0;
output_intf[1].cb.read <= 0;
output_intf[2].cb.read <= 0;
output_intf[3].cb.read <= 0;
input_intf.reset <= 0;
endtask : reset
task cfg_dut();
$display(" %0d
: Environment : start of cfg_dut() method",$time);
mem_intf.cb.mem_en <= 1;
@(posedge mem_intf.clock);
mem_intf.cb.mem_rd_wr <= 1;
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
@(posedge mem_intf.clock);
5/7
mem_intf.cb.mem_en <=0;
mem_intf.cb.mem_rd_wr <= 0;
mem_intf.cb.mem_add <= 0;
mem_intf.cb.mem_data <= 0;
endtask :cfg_dut
task start();
drvr.start();
rcvr[0].start();
rcvr[1].start();
rcvr[2].start();
rcvr[3].start();
sb.start();
join_any
%0d : Environment : end of start() method",$time);
$display("
endtask : start
task wait_for_end();
repeat(10000) @(input_intf.clock);
$display(" %0d : Environment : end
of wait_for_end() method",$time);
endtask : wait_for_end
task run();
$display("
%0d : Environment : start of run() method",$time);
build();
reset();
cfg_dut();
start();
wait_for_end();
report();
$display("
%0d : Environment : end of run() method",$time);
endtask: run
task report();
$display("\n\n*************************************************");
if(0==$root.error)
$display("********
TEST PASSED *********");
6/7
else
$display("******** TEST Failed with %0d errors *********",$root.error);
$display("*************************************************\n\n");
endtask : report
endclass
`endif
switch_7.tar
7/7
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_11_PHASE_8_COVERAGE.html
PHASE 8 COVERAGE
In this phase we will write the functional coverage for switch protocol. Functional
coverage is written in Coverage.sv file. After running simulation, you will analyze the
coverage results and find out if some test scenarios have not been exercised and write
tests to exercise them.
da :coverpoint pkt.da {
bins p0 ={`P0};
bins p1 ={`P1};
bins p2 ={`P2};
bins p3 ={`P3};}
all_cross:cross length,da,length_kind,fcs_kind;
1/4
functionnew();
switch_coverage =new();
endfunction:new
3) Write task which calls the sample method to cover the points.
switch_coverage.sample();
endtask:sample
`ifndefGUARD_COVERAGE
`defineGUARD_COVERAGE
class coverage;
packet pkt;
covergroup switch_coverage;
da :coverpoint pkt.da {
bins p0 ={`P0};
bins p1 ={`P1};
bins p2 ={`P2};
bins p3 ={`P3};}
all_cross:cross length,da,length_kind,fcs_kind;
endgroup
functionnew();
switch_coverage =new();
endfunction:new
switch_coverage.sample();
endtask:sample
endclass
`endif
2/4
coverage cov =new();
2) Call the sample method and pass the exp_pkt to the sample method.
cov.sample(pkt_exp);
`ifndef GUARD_SCOREBOARD
`define GUARD_SCOREBOARD
class Scoreboard;
mailbox drvr2sb;
mailbox rcvr2sb;
this.rcvr2sb = rcvr2sb;
endfunction:new
task start();
packet pkt_rcv,pkt_exp;
forever
begin
rcvr2sb.get(pkt_rcv);
drvr2sb.get(pkt_exp);
if(pkt_rcv.compare(pkt_exp))
begin
cov.sample(pkt_exp);
end
else
$root.error++;
end
endtask : start
endclass
`endif
3/4
switch_8.tar
4/4
WWW.TESTBENCH.IN - Easy Labs : SV
testbench.in/SL_12_PHASE_9_TESTCASE.html
PHASE 9 TESTCASE
Lets verify the DUT by sending large packets of length above 200.
endclass
small_packet spkt;
3) Pass the object of the small_packet to the packet handle which is in driver.
env.drvr.gpkt = spkt;
env.reset();
env.cfg_dut();
env.start();
env.wait_for_end();
env.report();
`ifndefGUARD_TESTCASE
`defineGUARD_TESTCASE
endclass
1/2
program testcase(mem_interface.MEM mem_intf,input_interface.IP
input_intf,output_interface.OP output_intf[4]);
Environment env;
small_packet spkt;
initial
begin
spkt =new();
env =new(mem_intf,input_intf,output_intf);
env.build();
env.drvr.gpkt
= spkt;
env.reset();
env.cfg_dut();
env.start();
env.wait_for_end();
env.report();
#1000;
end
final
endprogram
`endif
switch_9.tar
Report a Bug or Comment on This section - Your input is what keeps Testbench.in
improving with time!
2/2