Verification Suite using SystemVerilog – Functional Coverage – v2.0

Systemverilog verification suite of synchronous RAM along with functional coverage, my v1.0 suite can be found here

`timescale 1 ns/10 ps
 
module memory(input  logic           clock       ,       // Clock 
              input  logic  [7:0]    address     ,       // Memory address 
              input  logic  [7:0]    data_in     ,       // input data 
              output logic  [7:0]    data_out    ,       // Output data 
              input  logic           write       ,       // 1 is write mode, 0 is read mode 
              input  logic           enable              // Activate the circuit 
             );
 
 
logic [7:0] mem [255:0];
 
 
always @ (posedge clock)
begin
  if(enable)                        //Enable the circuit 
    if(write)                       //Write mode 
      mem[address] = data_in;
    else                            //Read mode 
      data_out = mem[address];
  else
    data_out = 8'd0;
end
 
endmodule

The interface:

	
`ifndef INTERFACE
`define INTERFACE

`timescale 1ns/10ps
 
// Input to the DUT is output to the interface
interface input_intf(input bit clock);
logic [7:0] address;
logic [7:0] data_in;
logic enable;
logic write;
int errors;
logic [7:0] data_out;
  logic read;
  
  modport dut_in(input clock, data_out);
  
  modport test_out(input clock, output address, data_in, write, enable,errors,read);

  covergroup mem @(posedge enable);
    
    option.per_instance = 1;
    a : coverpoint address;

    d: coverpoint data_in;
  endgroup
  covergroup outd @(posedge read);
    option.per_instance = 1;
    datao : coverpoint data_out;
  endgroup
   
  mem m = new;
  outd o = new;
endinterface

 
 
`endif

The interface contains definiton for a coverage group. “Functional coverage is a user-defined metric that measures how much of the design specification has been exercised in verification.”  A covergroup can contain one or more coverage points. A coverage point can be an integral variable or an integral expression. Each coverage point is associated with “bin”.On each sample clock simulator will increment the associated bin value.

Driver:

`ifndef DRIVER
`define DRIVER
 
`include "interface.sv"

class inp;
  rand bit[7:0] address;
  rand bit[7:0] data_in;
  constraint range1 {address > 0; address <255;}
  constraint range2 {data_in > 0; data_in < 255;}
endclass

class driver;
 
 
mailbox drvr2sb;
 

virtual input_intf.test_out out;
virtual input_intf.dut_in in;
   


  function new(virtual input_intf.test_out out_new ,  virtual input_intf.dut_in in_new, mailbox drvr2sb);
begin
this.out = out_new;
this.in = in_new;
this.drvr2sb = drvr2sb;
  //this.mem = new();
end
endfunction : new
 
task start();
begin
  reg [7:0] drv_data;
 inp mem_in = new;
    if(mem_in.randomize()) begin
      out.enable =1'b1;
      drv_data = mem_in.data_in;
      drvr2sb.put(drv_data);
      out.read = 1'b0;
      //$display(mem_in.address);
    write_to(mem_in.address,mem_in.data_in);

      repeat(3) @ (posedge out.clock)
        out.read = 1'b1;
      read(mem_in.address);
      out.read = 1'b0;
      out.enable =1'b0;
      //$display(out.data_out);
      end 
    else begin
   $display("Randomization Failed\n");
    end
end
endtask : start
 
task write_to (logic [7:0] address, logic [7:0] data_in);
begin
out.write = 1'b1;
out.address = address;
out.data_in = data_in;
  @(posedge out.clock)
out.write = 1'b0;
end
endtask : write_to
 
 
task read (logic [7:0] address);
begin
out.write = 1'b0;
out.address = address;
  @(posedge out.clock)
out.write = 1'b0;
end
endtask : read
 
 
endclass


`endif

The change from last time is that, it contains a class to randomize the signals, rather than $random. Class inp as address and data defined as rand bit and constraint defined as range1 and range2. The definition of range is not necessary because even without constraints the values are distributed uniformly over their range and it is unsigned.

Receiver:

`ifndef RECEIVER
`define RECEIVER
 
class receiver;

virtual input_intf.test_out out;
  virtual input_intf.dut_in in;
 
mailbox rcvr2sb;
 
  function new(virtual input_intf.test_out out_new,virtual input_intf.dut_in in_new, mailbox rcvr2sb);
begin
this.in= in_new;
this.out= out_new;
this.rcvr2sb =  rcvr2sb;
end
endfunction : new
 
task start();
begin

reg [7:0] rcvr_box;

  wait (out.read ==1'b1)
  repeat(2) @(posedge in.clock)
    rcvr_box = in.data_out;
    rcvr2sb.put(rcvr_box);
    $display("%0t : Output Monitor: Byte received from DUT: %b", $time, rcvr_box);
 
end
endtask
 
  
endclass
 
`endif

Scoreboard:

`ifndef SCOREBOARD
`define SCOREBOARD

class scoreboard;

mailbox drvr2sb;
mailbox rcvr2sb;

virtual input_intf.test_out out;
virtual input_intf.dut_in in;
  
  
  function new(mailbox drvr, mailbox rcvr, virtual input_intf.test_out new_out, virtual input_intf.dut_in new_in);
this.drvr2sb = drvr;
this.rcvr2sb = rcvr;
this.out = new_out;
this.in = new_in;
endfunction : new

task start();
begin
reg [7:0] sent_byte;
reg [7:0] recv_byte;


    drvr2sb.get(sent_byte);
    $display(" %0t : Scoreboard : byte %b received from driver", $time, sent_byte);

    rcvr2sb.get(recv_byte);
    $display(" %0t : scoreboard : byte %b received from output monitor", $time, recv_byte);

    if(sent_byte == recv_byte)
	begin
      $display("%0t : Scoreboard: Byte sent to DUT match byte received from DUT", $time);
	end

     else
	begin
	   $display(" %0t : Scoreboard : ERROR! Byte sent to DUT is not byte received from DUT", $time);
	   out.errors = out.errors + 1;
	end
end
endtask

endclass


`endif

Environment:

`ifndef ENVIRONMENT
`define ENVIRONMENT

`include "sb.sv"
 `include "drv.sv"
`include "rcv.sv"

class environment;
 

virtual input_intf.test_out out;
virtual input_intf.dut_in in;
 
driver drv;
mailbox drvr2sb;
receiver rcvr;
mailbox rcvr2sb;
scoreboard sb;
 
  function new(virtual input_intf.test_out out_new, virtual input_intf.dut_in new_in);
this.out = out_new;
this.in = new_in;
$display(" %0d : Environment : created env object", $time);
endfunction : new
 
function void build();
$display("%0d : Environment : start of build()", $time);
$display("%0d : Environment : end of build()", $time);
 
drvr2sb = new();
  drv = new(out,in,drvr2sb);
rcvr2sb = new();
  rcvr = new(out,in,rcvr2sb);
  sb = new(drvr2sb, rcvr2sb, out,in);
endfunction : build
 
task reset();
$display("%0d : Environment : start of reset()", $time);
out.address <= 0;
out.data_in <= 0;
out.enable <= 0;
out.write <= 0;
out.errors <= 0;
  repeat(4) @(posedge out.clock);
$display("%0d : Environment : end of reset()", $time);
endtask : reset
 
 
 
/*task cfg_dut();
$display("%0d : Environment : start of cfg_dut()", $time);
$display("%0d : Environment : end of cfg_dut()", $time);
endtask : cfg_dut
*/
 
task start();
  integer i;
  $display("%0t : Environment : start of start()", $time);
  for(i=0;i<1000;i=i+1) begin
    fork
      rcvr.start();
      sb.start();
    join_none
    drv.start();
    
    repeat (3) @(posedge out.clock);
      end
      $display("%0d : Environment : end of start()", $time);
endtask : start
 
 
task wait_for_end();
$display("%0d : Environment : start of wait_for_end()", $time);
$display("%0d : Environment : end of wait_for_end()", $time);
endtask : wait_for_end
 
task run();
$display("%0d : Environment : start of run()", $time);
build();
reset();
//cfg_dut();
start();
wait_for_end();
report();
$display("%0d : Environment : end of run()", $time);
endtask : run
 
task report();
$display("\n\n**************************************************");
  if(out.errors == 32'd0)
    $display("*************TEST PASSED**************");
else
  $display("**************Test failed with %0d errors ********", out.errors);
$display("***************************************************\n\n");
endtask : report
 
endclass
 
`endif

New additions are fork and join_none. The parent process continues to execute concurrently with all the processes spawned by the fork. The spawned processes do not start executing until the parent thread executes a blocking statement.

Testcase:

`ifndef TESTCASE
`define TESTCASE
`include "interface.sv"
`include "environment.sv"
 
program testcase(input_intf.test_out out, input_intf.dut_in in);
environment env;
initial
begin
$display("**********Start of Testcase*************");
  env = new(out, in);
env.run();
#1000;
$display("*************End of Testcase**************");
end
endprogram
 
`endif

Top:

`ifndef TOP
`define TOP

`include "tst.sv"
 
`timescale 1ns/10ps
 
module top();
bit clock;
 
  input_intf in(clock);
 

 
  testcase tc(in.test_out, in.dut_in);
 
  memory dut (in.clock,in.address, in.data_in, in.data_out, in.write, in.enable);
 
initial clock =1'b0;
always #5 clock = ~clock;
 
endmodule
`endif

The output for this can be found on EDA Playground, run the program here

The coverage report:

CoverageReport

Coverpoint a => Address. Coverpoint d => Data_in. Coverpoint outd => Data_out

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.