sv_labs learning notes - sv_lab5_ Upper (System Verilog)

This section will introduce the first part of lab5, which mainly summarizes the ways of learning and thinking about general design and the points that need to be emphasized. At the same time, taking lab5 as a reference, it will analyze the flow direction of data flow, verify the communication and abstraction of components, and realize the overall idea.

Lab5 braod spectrum verification (Part I)

Experimental objectives

First, clarify the experimental objectives. According to the experimental guidance in lab4, we have realized the data transmission and detection of the specified port. In this section of the lab, we will realize 16 channels to send data at the same time and monitor 16 groups of output chanel at the same time. The validation component is further encapsulated in the form of class. Further improve our verification structure.

Train of thought combing

We have defined the design goal, and we can obviously feel that lab5 is more abstract than the previous one. According to the most basic verification component framework, combined with rest SV code to sort out the data flow and the approximate sequence of program operation. The more detailed part will be explained and combed in the module analysis of the design.
You need to understand two basic concepts, mailbox and semaphore. Simply put, mailbox is used for data interaction between threads. Semaphore implements access control of the same resource. Refer to the author's Communication between System Verilog threads
The first is the generation of data, which is generated by the Generator in the form of Package and put into out_box is sent to the Driver. The Driver will send data from the corresponding drvr according to sa, and Package the data to the Driver of scoreboard_ Mbox, compare the data by the scoreboard, and then rcvr receive it to the receiver of the scoreboard_ Mbox, compare the data. The specific comparison method will be described in subsequent codes.
There are some points that need to be considered here

  • gen data generation form and data format.
  • How to solve the problem of sending multiple sd's to one da
  • How does the Driver use the data generated by gen to complete data transmission
  • How does scoreboard compare data
  • How does the test environment stop the simulation

This blog post will analyze the generation, transmission and overall verification structure of data. The next chapter analyzes data monitoring and data comparison. file

set rtl ../../rtl/router.v 
set svtb {./ ./ ./}
vlib work
vmap work work
vlog $rtl
vlog -sv ./ ./ ./
vsim -t ns -novopt +notimingchecks -l router_test_top.log work.router_test_top
run -all

code analysis

First, look at the top layer of the test. The top layer instantiates the interface and test program. These are the same as before and will not be explained here. It is important to analyze the test program. In order to ensure the integrity of the code, the analysis will show all the code.

`timescale 1ns/100ps
module router_test_top;
  parameter simulation_cycle = 100;

  bit SystemClock;//bit is binary logic. There are only 0 and 1. The initial value is 0 by default
  router_io top_io(SystemClock);
  test t(top_io);
  router dut(
    .reset_n	(top_io.reset_n),
    .clock		(top_io.clock),
    .din		(top_io.din),
    .frame_n	(top_io.frame_n),
    .valid_n	(top_io.valid_n),
    .dout		(top_io.dout),
    .valido_n	(top_io.valido_n),
    .busy_n		(top_io.busy_n),
    .frameo_n	(top_io.frameo_n)

  initial begin
    $timeformat(-9, 1, "ns", 10);
    SystemClock = 0;
    forever begin
        SystemClock = ~SystemClock;



Mailbox is a communication mechanism, which enables data to be transmitted and communicated between processes. Data is sent by one process to another mailbox, and another process can obtain it.
mailbox #(type = dynamic_type)
Where dynamic_type represents a special type that can perform type checking at run time (by default). Refer to the author's Communication between System Verilog threads

typedef class Packet;
typedef mailbox #(Packet) pkt_mbox;

`timescale 1ns/100ps
interface router_io(input bit clock);
  logic		reset_n;
  logic [15:0]	din;
  logic [15:0]	frame_n;
  logic [15:0]	valid_n;
  logic [15:0]	dout;
  logic [15:0]	valido_n;
  logic [15:0]	busy_n;
  logic [15:0]	frameo_n;

  clocking cb @(posedge clock);
    //default input #1 output #1;
    output reset_n;
    output din;
    output frame_n;
    output valid_n;
    input dout;
    input valido_n;
    input busy_n;
    input frameo_n;

  modport TB(clocking cb, output reset_n);

16 test programs are instantiated, including semaphore, Driver and Receiver, and one Generator and Scoreboard
. Here, all the corresponding data (out_boxes) generated by gen are sent to the mailbox of each instantiated drvr [] and each drvr [] is copied and put into the mailbox of the drvr itself. drvr completes the transmission and puts the transmitted data into the driver_mbox, the sending is completed

`timescale 1ns/100ps
program automatic test(router_io.TB rtr_io);
  `include ""
  int run_for_n_packets;      // number of packets to test
  int TRACE_ON = 1;

  `include "router_test.h"
  `include ""
  `include ""
  `include ""
  `include ""

  semaphore sem[];                    //Declare dynamic array
  Driver drvr[];                         // driver
  Receiver rcvr[];                     // receiver
  Generator gen;                        // generator
  Scoreboard sb;                         // scoreboard

  initial begin  
    run_for_n_packets = 200;
	sem = new[16];  				//Allocate 16 elements (16 handles)
	drvr = new[16];
	rcvr = new[16];
	gen = new("gen");
	sb = new("sb");

	foreach (sem[i])
	  sem[i] = new(1);//Assign a key to each semaphore
	foreach (drvr[i])
	  drvr[i] = new($sformatf("drvr[%0d]",i), i, sem, gen.out_box[i], sb.driver_mbox, rtr_io);
	foreach (rcvr[i])
	  rcvr[i] = new($sformatf("rcvr[%0d]",i), i, sb.receiver_mbox, rtr_io);
//The above processes do not consume time, so they have been completed at simulation time 0. Complete communication and between components
	foreach (drvr[i]) 
	foreach (rcvr[i]) 
		rcvr[i].start();//The start function takes no time, so it is also executed at the same time after the reset. You can view the print information yourself
	wait(sb.DONE.triggered);//Wait for scoreboard end event to be triggered
  task reset();
    if (TRACE_ON) $display("[TRACE]%t :%m", $realtime);
    rtr_io.reset_n = 1'b0;
    rtr_io.cb.frame_n <= '1;
    rtr_io.cb.valid_n <= '1;
    #2 rtr_io.cb.reset_n <= 1'b1;
    repeat(15) @(rtr_io.cb);
  endtask: reset

endprogram: test

%m will print the directory structure
out_box [] will pass in the corresponding drvr and send it, which is also the logic of data generation. There are 16 groups_ box. Each group is sent to the corresponding drvr. Each group of SAS is put into the sa me out_box.
Packet pkt = new this.pkt2send;// Copy a copy of pkt2send to pkt. This sentence involves shallow copy through new. See Chapter 5.15 of the green paper
Copying an object using the new operator is simple and reliable. It creates a new object and copies all the variables of the existing object. This is a simple copy, which is similar to a photocopy of the original object. The value of the original object is blindly copied to the destination object. If a class contains a handle to another class, only the objects at the highest level will be copied by the new operator, and the objects at the lower level will not be copied.

class Generator;
  string  name;		// unique identifier
  Packet  pkt2send;	// stimulus Packet object
  pkt_mbox out_box[];	// mailbox to Drivers

  extern function new(string name = "Generator");
  extern virtual task gen();
  extern virtual task start();
endclass: Generator

function Generator::new(string name);
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, name); = name;
  this.pkt2send = new();
  this.out_box = new[16];//Allocate 16 elements (16 handles)
    this.out_box[i] = new();//Add each mailbox new
endfunction: new

task Generator::gen();
  static int pkts_generated = 0;
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,; = $psprintf("Packet[%0d]", pkts_generated++);
  if (!this.pkt2send.randomize()) begin
    $display("\n%m\n[ERROR]%t Randomization Failed!\n", $realtime);
endtask: gen

task Generator::start();
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,;
    for (int i=0; i<run_for_n_packets || run_for_n_packets <= 0; i++) begin
        Packet pkt = new this.pkt2send;//Copy a copy of pkt2send to pkt
        this.out_box[].put(pkt);//Send to the corresponding out according to the sa of the data itself_ In box
  join_none//In order not to be in rest Block the following processes in SV and use join_none.  However, it is also possible to change it to join here, because there is no delay statement here
endtask: start

The function of DriverBase is to send the data in pkt2send to the corresponding da port, which is the same as the previous implementation. Send data to the interface.

class DriverBase;
  virtual router_io.TB rtr_io;	// interface signal
  string    name;		// unique identifier
  bit[3:0]  sa, da;		// source and destination addresses
  logic[7:0]  payload[$];		// Packet payload
  Packet    pkt2send;		// stimulus Packet object

  extern function new(string name = "DriverBase", virtual router_io.TB rtr_io);
  extern virtual task send();
  extern virtual task send_addrs();
  extern virtual task send_pad();
  extern virtual task send_payload();

function DriverBase::new(string name, virtual router_io.TB rtr_io);
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, name);   = name;
  this.rtr_io = rtr_io;

task DriverBase::send();
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,;

task DriverBase::send_addrs();
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,;
  this.rtr_io.cb.frame_n[] <= 1'b0;
  for(int i=0; i<4; i++) begin
    this.rtr_io.cb.din[] <= this.da[i];

task DriverBase::send_pad();
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,;
  this.rtr_io.cb.din[] <= 1'b1;
  this.rtr_io.cb.valid_n[] <= 1'b1;
  repeat(5) @(this.rtr_io.cb);

task DriverBase::send_payload();
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,;
  foreach(this.payload[index]) begin
    for(int i=0; i<8; i++) begin
      this.rtr_io.cb.din[] <= this.payload[index][i];
      this.rtr_io.cb.valid_n[] <= 1'b0;
      this.rtr_io.cb.frame_n[] <= ((index == (this.payload.size() - 1)) && (i == 7));
  this.rtr_io.cb.valid_n[] <= 1'b1;

Driver .sv

Copy the data generated at the top level to each drvr instantiated. Then sem came in. Each instantiated Driver can send data and has its own ID. Assuming the ID is 3, only data with sa=3 can be sent.
this.in_box.get(this.pkt2send); Here, take out a copy of the incoming verse and put it in pkt2send. Next, determine whether the sa sending port is the current port.
if ( != continue;
Note the continue here. When the sa of the comparison data is inconsistent with the current port, continue is to exit the current cycle and start the next cycle. Equivalent to this data

`include ""
class Driver extends DriverBase;
  pkt_mbox in_box;	// Generator mailbox
  pkt_mbox out_box;	// Scoreboard mailbox
  semaphore sem[];	// output port arbitration

  extern function new(string name = "Driver", int port_id, semaphore sem[], pkt_mbox in_box, out_box, virtual router_io.TB rtr_io);
  extern virtual task start();

function Driver::new(string name, int port_id, semaphore sem[], pkt_mbox in_box, out_box, virtual router_io.TB rtr_io);, rtr_io);
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,; = port_id;//There are 16 IDS in total. These IDS correspond to 16 SAS, that is, one SA has one sending incentive. The DRIVER label corresponding to SA is the same. If SA is 1, the corresponding instantiated transmission data is used
  this.sem = sem;//Pass the sem of the top layer of the test into the internal
  this.in_box = in_box;//Copy the data generated at the top level to each drvr instantiated
  this.out_box = out_box;
endfunction: new

task Driver::start();
  if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime,;
    forever begin
	  this.in_box.get(this.pkt2send);//Get the packet content from the copied data
	  if ( != continue;//From in_ Get relevant sending information in box. Including where it comes from, where it goes, and how many related information it contains. If the Driver ID is different from it, it will be put to the next judgment. Otherwise, it will exit to receive the next message.
	  //In fact, there is no need to judge here, because the data sa in the incoming mailbox corresponds to the ID currently sent. See Generator
	  this.da = this.pkt2send.da;
	  this.payload = this.pkt2send.payload;
	  this.sem[this.da].get(1);//Here, multiple SAS are prevented from sending the same da. Protect. When multiple SAS send data to a DA, they will block, forming a protection.
	  this.out_box.put(this.pkt2send);//Put the sent data into out_ In box
endtask: start

Keywords: systemverilog

Added by ROCKINDANO on Fri, 07 Jan 2022 08:22:00 +0200