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:
Coverpoint a => Address. Coverpoint d => Data_in. Coverpoint outd => Data_out