// Memory RTL Code
module memory (addr,cs,re,we,clk,reset,data_in,data_out);
parameter addr_width=8; parameter data_width=8;
input [addr_width-1:0] addr;
input cs; input re,we;
input clk,reset;
input [data_width-1:0] data_in;
output [data_width-1:0] data_out;
reg [data_width-1:0] data_out;
reg [data_width-1:0] mem [0:2**addr_width];
integer i;
always@(posedge clk)
begin: MEM
if(reset) begin
mem[addr]<=0;
end else begin
if(cs) begin
if(we) begin
mem[addr] <=data_in;
end
if(re) begin
data_out <=mem[addr];
end
end
end
end
endmodule
//Mem_gen.sv
/ /class generator
class mem_trans_gen #(addr_width=`awidth, data_width=`dwidth);
transaction mem_trans, mem_trans_mb;
mailbox gen2mbox;
function new(mailbox inbox);
this.gen2mbox=inbox;
endfunction
extern task run(string test_name);
endclass
// generator task
task mem_trans_gen :: run(string test_name);
begin
if (test_name == "single_wr_rd") begin
repeat (`no_of_trans) begin
mem_trans =new();
mem_trans_mb=new;
// Write transaction
assert(mem_trans.randomize() with {trans_type==WRITE;});
[Link](mem_trans);
$display("Generator addr=%h wr_data = %h, trans_type=%s \n", mem_trans.addr,
mem_trans.wr_data, mem_trans.trans_type.name());
// Read transaction
assert(mem_trans_mb.randomize() with {addr==mem_trans.addr; trans_type==READ;});
[Link](mem_trans_mb);
$display("Generator addr=%0h, rd_data=%0h, trans_type=%s \n", mem_trans_mb.addr,
mem_trans_mb.rd_data, mem_trans_mb.trans_type.name());
end
$display("Gen -mbox size=%0d", [Link]());
end
end
endtask
//Mem_drv.SV
// class mem_driver
class mem_trans_drv #(addr_width=`awidth, data_width=`dwidth);
transaction mem_trans, mem_trans_mb;
mailbox mbox2drv;
virtual mem_if mem_if_drv;
function new(mailbox inbox, virtual mem_if v_if);
this.mbox2drv=inbox;
this.mem_if_drv=v_if;
endfunction
extern task run();
extern task send_to_dut(transaction trans_drv);
endclass
// driver run task
task mem_trans_drv :: run;
begin
mem_trans =new();
mem_trans_mb=new;
@(negedge mem_if_drv.reset);
$display("driver-mbox size=%0d", [Link]());
repeat (`no_of_trans) begin
// Write transaction
[Link](mem_trans_mb);
send_to_dut(mem_trans_mb);
// read transaction
// $display("driver-mbox size=%0d", [Link]());
[Link](mem_trans_mb);
send_to_dut(mem_trans_mb);
end
end
endtask
// driver send_to_dut task
task mem_trans_drv :: send_to_dut(input transaction trans_drv);
begin
// write
@(posedge mem_if_drv.clk)
if (trans_drv.trans_type==WRITE) begin
$display($time, " Drive to dut WRITE addr=%0h, wr_data = %h, trans_type=%s \
n",trans_drv.addr, trans_drv.wr_data, trans_drv.trans_type.name());
mem_if_drv.mem_drv.cs=1;
mem_if_drv.mem_drv.we=1;
mem_if_drv.mem_drv.re=0;
mem_if_drv.mem_drv.addr=trans_drv.addr;
mem_if_drv.mem_drv.data_in=trans_drv.wr_data;
@(posedge mem_if_drv.clk)
mem_if_drv.mem_drv.we=0;
mem_if_drv.mem_drv.cs=0;
end
//read
if (trans_drv.trans_type==READ) begin
mem_if_drv.mem_drv.cs=1;
mem_if_drv.mem_drv.we=0;
mem_if_drv.mem_drv.re=1;
mem_if_drv.mem_drv.addr=trans_drv.addr;
repeat (2) @(posedge mem_if_drv.clk)
trans_drv.rd_data= mem_if_drv.mem_drv.data_out;
mem_if_drv.mem_drv.re=0;
mem_if_drv.mem_drv.cs=0;
$display($time, " Driver to dut READ addr=%0h, rd_data = %h, trans_type=%s \n",trans_drv.addr,
trans_drv.rd_data, trans_drv.trans_type.name());
end
end
endtask
//mem_if.SV
// interface for memory
interface mem_if #(awidth=`awidth, dwidth=`dwidth) (input bit clk, reset);
logic [awidth-1:0] addr;
logic [dwidth-1:0] data_in, data_out;
logic cs,re,we;
modport mem_drv (input data_out, output addr, data_in, cs, re, we);
modport mem_mon (input data_out, addr, data_in, cs, re, we);
// assertions for protocol check
property bus_check;
@(negedge clk) disable iff (reset| ~cs)
(cs & we & ~re) or (cs & ~we & re);
endproperty
property rd_check;
@(negedge clk) disable iff (reset|~cs)
(cs & ~we & re) |=> (cs & ~we & re) and $stable(addr);
endproperty
// asserting the property
assert property (bus_check);
assert property (rd_check);
endinterface: mem_if
//mem_tb.v
//memory_tb.sv : testbench
`timescale 1ns/1ps
// include RTL file
//`include "memory.v"
// include test bench files
`include "[Link]"
import globals::*;
`include "mem_gen.sv"
`include "mem_if.sv"
`include "mem_drv.sv"
`include "mem_mon.sv"
`include "mem_sb.sv"
`include "mem_test.sv"
// memory testbench module
module memory_tb;
reg clk, reset;
wire dut_clk;
// memory interface
mem_if mem_if_r(clk, reset); // physical interface
// Memory(DUT) instantiation
memory #(`awidth, `dwidth)
memory_inst(mem_if_r.addr,mem_if_r.cs,mem_if_r.re,mem_if_r.we,dut_clk,mem_if_r.reset,mem_if
_r.data_in,mem_if_r.data_out);
// test instantiation using program block
mem_test test(mem_if_r); // binding physical to virtual interface
// clock generation
initial begin
clk = 0;
forever
#5 clk = ~clk;
end
assign #1 dut_clk = clk;
//reset generation
initial begin
reset = 1;
mem_if_r.addr=0;
mem_if_r.cs=0;
mem_if_r.re=0;
mem_if_r.we=0;
#12
reset = 0;
end
//dump waves
initial begin
$dumpvars;
$dumpfile("[Link]");
//$recordfile("[Link]");
//$recordvars;
end
endmodule
//[Link]: global defines
package globals;
typedef enum {WRITE, READ} trans;
typedef enum {PATRN_AA, PATRN_55, WALK_1, WALK_0} vec_pattern;
`define awidth 16
`define dwidth 32
`define no_of_trans 100
`define timeout 1000000
// class transaction
class transaction #(addr_width=`awidth, data_width=`dwidth);
rand bit [addr_width-1:0] addr;
rand bit [data_width-1:0] wr_data;
bit [data_width-1:0] rd_data;
rand trans trans_type;
constraint addr_constr {addr[1:0] == 0;}
endclass
endpackage
//mem_test.sv : test
program mem_test (mem_if mem_if_t);
// mbox for gen 2 driver constrcution
mailbox gen2drv=new();
// mbox for monitor 2 scoreboard
mailbox mon2sb=new();
// memory gen construction
mem_trans_gen trans_gen=new(gen2drv);
// memory driver construction
mem_trans_drv trans_drv=new(gen2drv, mem_if_t);
// memory monitor construction
mem_trans_mon trans_mon=new(mon2sb, mem_if_t);
// memory scoreboard construction
mem_trans_sb trans_sb=new(mon2sb);
// tasks call for each test
initial begin
// Test 1 - single wr and read
trans_gen.run("single_wr_rd");
// Test 2 - multiple wr and read
// Test 3 - All locations wr and read
// Test 4 - All locations wr and read with pattern
// Test 5 - Walking 1's and walking 0s
fork
// run driver
trans_drv.run;
// run monitor
trans_mon.run;
//run scoreboard
trans_sb.run;
join
end
// timeout for tests
initial begin
#`timeout;
trans_sb.data_check;
$exit;
end
endprogram
// mem_mon.sv: class mem monitor
class mem_trans_mon #(addr_width=`awidth, data_width=`dwidth);
transaction mem_trans_mb;
mailbox mon2mbox;
virtual mem_if mem_if_m;
// functional coverage
covergroup mem_trans_cov;
//coverpoint mem_trans_mb;
option.per_instance=1;
option.at_least=1;
addr: coverpoint mem_trans_mb.addr {
bins addr_low={[0:'h00ff]};
bins addr_med={[16'h0100:16'h0fff]};
bins addr_high={[16'h1000:16'hffff]};
w_data:coverpoint mem_trans_mb.wr_data {
bins wr_data_low={[0:32'h00ff_ffff]};
bins wr_data_med={[32'h0100_0000:32'h0fff_ffff]};
bins wr_data_high={[32'h1000_0000:32'hffff_ffff]};
r_data:coverpoint mem_trans_mb.rd_data {
bins rd_data_low={[0:32'h00ff_ffff]};
bins rd_data_med={[32'h0100_0000:32'h0fff_ffff]};
bins rd_data_high={[32'h1000_0000:32'hffff_ffff]};
trans_type:coverpoint mem_trans_mb.trans_type {
bins write = {WRITE};
bins read ={READ};
addr_x_trans_type : cross addr, trans_type;
endgroup
function new(mailbox inbox, virtual mem_if v_if);
this.mon2mbox=inbox;
this.mem_if_m=v_if;
this.mem_trans_cov=new;
endfunction
extern task run();
endclass
// montor run task
task mem_trans_mon :: run;
integer count=0;
begin
forever begin
// write trans capture
@(posedge mem_if_m.clk)
if (mem_if_m.mem_mon.cs & mem_if_m.mem_mon.we & ~mem_if_m.mem_mon.re) begin
mem_trans_mb=new;
mem_trans_mb.addr=mem_if_m.mem_mon.addr;
mem_trans_mb.wr_data=mem_if_m.mem_mon.data_in;
mem_trans_mb.trans_type=WRITE;
[Link](mem_trans_mb); //put write trans to mailbox
$display($time, " Monitor WRITE addr=%0h wr_data = %0h, trans_type=%s \n",
mem_trans_mb.addr, mem_trans_mb.wr_data, mem_trans_mb.trans_type.name());
mem_trans_cov.sample();
end
// read trans capture
if (mem_if_m.mem_mon.cs & ~mem_if_m.mem_mon.we & mem_if_m.mem_mon.re) begin
count++;
if (count%2==0) begin
mem_trans_mb=new;
mem_trans_mb.addr=mem_if_m.mem_mon.addr;
mem_trans_mb.rd_data=mem_if_m.mem_mon.data_out;
mem_trans_mb.trans_type=READ;
[Link](mem_trans_mb); //put read trans to mailbox
$display($time, " Monitor READ addr=%0h rd_data = %0h, trans_type=%s count=%0d\n",
mem_trans_mb.addr, mem_trans_mb.rd_data, mem_trans_mb.trans_type.name(), count);
count=0;
mem_trans_cov.sample();
end
end
end
end
endtask
// mem_sb.sv: class mem scoreboard
class mem_trans_sb #(addr_width=`awidth, data_width=`dwidth);
transaction mem_trans_mb;
bit [data_width-1:0] wr_data[integer];
bit [data_width-1:0] rd_data[integer];
mailbox mbox2sb;
function new(mailbox inbox);
this.mbox2sb=inbox;
endfunction
extern task run();
extern task data_check();
endclass
// scoreaboard run task
task mem_trans_sb :: run;
integer count=0;
begin
mem_trans_mb=new;
forever begin
[Link](mem_trans_mb);
$display($time, " Score Board addr=%0h wr_data = %0h, rd_data=%0h, trans _type=%s \n",
mem_trans_mb.addr, mem_trans_mb.wr_data, mem_trans_mb.rd_data,
mem_trans_mb.trans_type.name());
if (mem_trans_mb.trans_type==WRITE)
wr_data[mem_trans_mb.addr]= mem_trans_mb.wr_data;
else
rd_data[mem_trans_mb.addr]= mem_trans_mb.rd_data;
end
end
endtask
// data check task
task mem_trans_sb :: data_check;
bit [addr_width-1:0] addr_t;
integer no_wr_entries, no_rd_entries;
begin
no_wr_entries=wr_data.num();
no_rd_entries=rd_data.num();
for (addr_t=0; addr_t< 2**addr_width; addr_t++) begin
if (wr_data.exists(addr_t)) begin
if (wr_data[addr_t] === rd_data[addr_t])
$display($time, " Addr=%0h, Write data=%0h MATCHED with READ data=%0h", addr_t,
wr_data[addr_t], rd_data[addr_t]);
else
$display($time, " ERROR-Addr=%0h, Write data=%0h NOT MATCHED with READ data=%0h",
addr_t, wr_data[addr_t], rd_data[addr_t]);
no_wr_entries--;
no_rd_entries--;
$display($time, " Write entries=%0d Read entries=%0d", no_wr_entries, no_rd_entries);
end //if
if (no_wr_entries == 0)
break;
end //for
end
endtask