For Dpram . how i can write a constraint block using both random write and random read signals such that write should come first and later read signal

Sir, I am very new to system verilog and i have started writing code for dpram as shown below, I am facing 2 problems

  1. At constraint block, as my dpram specification is write enable(w_en)should be held high such that i can load randomdata(w_data)in random addresses(w_addr). i have tried writing using constraint block such that write(w_en)should come first and later(r_en) when i randomize. but i failed.
  2. I am unable to get my output(r_data)when am retrieving it from virtual interface from dut at driver display. Also i have synchronizing problem with driver and monitor. please do check the code in driver and monitor and let me know, where I am making mistake. So that i wont repeat such mistakes in writing other codes. thanks

/////////////////////////////////////////////////////DUT///////////////////////////////////////////////////////////////////////
module dpram( clk,rst,w_en,r_en,w_addr,r_addr,w_data,r_data);
input clk,rst,w_en,r_en;
input [1:0]w_data;
input [1:0]w_addr,r_addr;
output reg [1:0]r_data;
reg [1:0]mem[4];

always@(posedge clk)
begin
if(rst)
begin
for(int i = 0; i< 4; i++) begin
mem [i]<= 0;
end
end
else
begin
if (w_en)
mem[w_addr]<=w_data;
if (r_en)
r_data<=mem[r_addr];
end
end
endmodule

///////////////////////////////////////////////INTERFACE///////////////////////////////////////////////////////////////////////////

interface intf(input logic clk,rst);
logic [1:0]w_addr;
logic [1:0]r_addr;
logic [1:0]w_data;
logic [1:0]r_data;
logic w_en,r_en;

clocking driver_cb@(posedge clk);
default input #1 output #1;
input r_data;
output r_en;
output w_en;
output r_addr;
output w_data;
output w_addr;
endclocking

clocking monitor_cb@(posedge clk);
default input #1 output #1;
input r_data;
input w_data;
input r_en;
input w_en;
input w_addr;
input r_addr;
endclocking

endinterface

///////////////////////////////////////////TRANSACTION//////////////////////////////////////////////////////////////////

class transaction;
rand bit [1:0]w_addr;
rand bit [1:0]r_addr;
randc bit [1:0]w_data;
rand bit w_en;
rand bit r_en;
bit [1:0]r_data;
bit [1:0]cnt;

constraint wdc {w_en != r_en;}
constraint cond {unique{w_en,r_en};}

function void display(string name); $display(“[%0t],w_addr:%b,r_addr:%b,w_data:%b,w_en:%b,r_en:%b,r_data:%b”,$time,w_addr,r_addr,w_data,w_en,r_en,r_data);
endfunction

endclass

//////////////////////////////////////////GENERATOR///////////////////////////////////////////////////////////////////////////////

class generator;

rand transaction trans;
int repeat_count=8;
// Create a mailbox handle to put items
mailbox gen2driv;
event ended;

function new(mailbox gen2driv);
this.gen2driv=gen2driv;
endfunction

task send ();
  $display("---------------------GENERATOR STARTED---------------");
  for (int i = 0; i < repeat_count; i++) begin
    trans =new();
    if(!trans.randomize)
    $fatal("Gen: randomization failed");
      trans.display("generator");
      gen2driv.put(trans);      
	end
  -> ended;
endtask
endclass

/////////////////////////////////////////////DRIVER///////////////////////////////////////////////////////////////////////////////

class driver;

mailbox gen2driv;
int no_transactions;
virtual intf vif;

function new(virtual intf vif,mailbox gen2driv);
this.vif=vif ;
this.gen2driv=gen2driv;
endfunction

task reset;
wait(vif.rst);
$display(“[ DRIVER ]---------------- Reset Started ------------------”);
vif.driver_cb.w_addr <= 0;
vif.driver_cb.r_addr <= 0;
vif.driver_cb.w_data <=0;
vif.driver_cb.w_en <= 0;
vif.driver_cb.r_en <= 0;
$display(“reset:%0t, w_en:%b,r_en:%b,w_addr:%b,w_data:%b”,$time,vif.w_en,vif.r_en,vif.w_addr,vif.w_data);
wait(!vif.rst);
$display(“[ DRIVER ]----------------- Reset Ended ---------------------”);
$display(“reset:%0t, w_en:%b,r_en:%b,w_addr:%b,w_data:%b”,$time,vif.w_en,vif.r_en,vif.w_addr,vif.w_data);
endtask

task receive ();
  transaction trans;
  $display("---------------------DRIVER STARTED---------------");
  forever begin
      gen2driv.get(trans);
      @(vif.driver_cb);
      vif.w_addr<=trans.w_addr;
      vif.r_addr<=trans.r_addr;
    if(trans.w_en) begin
        vif.driver_cb.w_en <= trans.w_en;
        vif.driver_cb.w_addr <= trans.w_addr;
        vif.driver_cb.w_data <= trans.w_data;
      $display("[%0t] WENABLE- w_en:%b,r_en:%b,w_addr:%b,w_data:%b,r_data:%b",$time,trans.w_en,trans.r_en,trans.w_addr,trans.w_data,trans.r_data);
      end
    if(trans.r_en) begin
        vif.driver_cb.r_en <= trans.r_en;
        vif.driver_cb.r_addr <= trans.r_addr;
        trans.r_data <= vif.driver_cb.r_data;
      $display("[%0t] RENABLE r_en:%b,w_en:%b,w_addr:%b,w_data:%b,r_data:%b",$time,trans.r_en,trans.w_en,trans.w_addr,trans.w_data,trans.r_data);
      end
      no_transactions++;
  
  end
endtask

endclass

///////////////////////////////////////////MONITOR////////////////////////////////////////////////////////////////////////////

class monitor;

virtual intf vif;
mailbox mon2scb;

function new (virtual intf vif, mailbox mon2scb);
this.vif=vif;
this.mon2scb=mon2scb;
endfunction

task send();

$display("--------------------- MONITOR STARTED---------------");   
forever begin
  transaction trans;
  trans=new();
  @( vif.monitor_cb);
  if(vif.monitor_cb.w_en) begin
    trans.w_addr  = vif.monitor_cb.w_addr;
    trans.w_en = vif.monitor_cb.w_en;
    trans.w_data = vif.monitor_cb.w_data;
  end
    if(vif.monitor_cb.r_en) begin
      trans.r_en = vif.monitor_cb.r_en;
      trans.r_addr  = vif.monitor_cb.r_addr;     
      trans.r_data = vif.monitor_cb.r_data;
    end
 // $display("[%0t] monitor checking r_en:%b,w_en:%b,w_addr:%b,w_data:%b,r_data:%b",$time,vif.r_en,vif.w_en,vif.w_addr,vif.w_data,vif.r_data); 
  mon2scb.put(trans);

// trans.display(“[monitor]”);
end
endtask

endclass
//////////////////////////////////////////////////ENVIRONMENT//////////////////////////////////////////////////////////////////////

class environment;

mailbox gen2driv;
    mailbox mon2scb;
generator gen;
driver  driv;
    monitor   mon;
    virtual intf vif;

    event ended;

function new(virtual intf vif);
this.vif=vif;
gen2driv=new();
mon2scb=new();
gen =new(gen2driv);
driv =new(vif,gen2driv);
mon =new(vif,mon2scb);
endfunction

task pre_test();
driv.reset();
endtask

task test();
fork
gen.send();
driv.receive();
mon.send();
join_any
endtask

task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask

task run;
pre_test();
test();
post_test();
$finish;

endtask
endclass

/////////////////////////////////////////////////PROGRAM////////////////////////////////////////////////////////////////////

program test(intf i_intf);

environment env;
initial begin
env=new(i_intf);
env.run();
end
endprogram

/////////////////////////////////////////////////TESTBENCHTOP//////////////////////////////////////////////////////////////////////

module top();
bit clk;
bit rst;
always #5 clk = ~clk;
initial begin
rst = 1;
#10 rst =0;
end
//creatinng instance of interface, inorder to connect DUT and testcase
intf i_intf(clk,rst);
//Testcase instance, interface handle is passed to test as an argument
test t1(i_intf);
//DUT instance, interface signals are connected to the DUT ports
dpram DUT (.clk(i_intf.clk),
.rst(i_intf.rst),
.w_en(i_intf.w_en),
.r_en(i_intf.r_en),
.w_addr(i_intf.w_addr),
.r_addr(i_intf.r_addr),
.w_data(i_intf.w_data),
.r_data(i_intf.r_data));
initial begin
$dumpfile(“dump.vcd”); $dumpvars;
end
endmodule
////////////////////////////////////////////////////////////////////////////////////////////////////

In reply to subbarao:
On your question how i can write a constraint block using both random write and random read signals such that write should come first and later read signal.
I suggest that you add an associative array that keeps track of locations that were ever written. Below is a snippet of code. The mem_aarray[addr] needs to be updated with data upon a write.


  int mem_aarray[*]; // associative array (AA), stores address
  constraint wdc {w_en != r_en;
                  mem_aarray.exists(addr) ==0 -> r_en==0;}
           constraint cond {unique{w_en,r_en};}

FYI, I used this approach in my SVA book to check the integrity of a memory. Below is the code. Though I did not answer all your questions, I hope that this will provide an insight into a workable approach.


module memory_data_integrity_check (
    input bit write, // memory write
    input bit read,  // memory read
    input bit[31:0] wdata, // data written to memory
    input bit[31:0]  addr,  // memory address -- small for simulation 
    input bit reset_n, // active low reset
    input bit clk);   // clock 

    timeunit 1ns;   timeprecision 100ps;
    default clocking cb_clk @ (posedge clk);  endclocking 
    int mem_aarray[*]; // associative array (AA) to be used by property
    bit [31:0] rdata;  // data read from memory
    bit  mem_aarray_exists;  // exists at specified address
    assign mem_aarray_exists  = mem_aarray.exists(addr); 
    always_comb 
        if(mem_aarray_exists) 
            rdata  = mem_aarray[addr];  //  
    always@ (posedge clk)
        if (reset_n==1'b0) mem_aarray.delete; // Clears AA elements
        else if (write) mem_aarray[addr]  = wdata; // store data

    property p_read_after_write;
        bit [31:0] v_wrdata, v_wraddr;
        (write, v_wraddr=addr, v_wrdata= wdata) |-> // if write, save data and addr
        (read && mem_aarray_exists && addr==v_wraddr) [->1] |-> // @read and data there at same written addr
        rdata==v_wrdata;  // read data is same as what was written 
    endproperty : p_read_after_write
    ap_read_after_write : assert property (p_read_after_write)
        $info("addr =%h, rdata=%h", $sampled(addr), $sampled(rdata)); else 
        $error("addr =%h, rdata=%h", $sampled(addr), $sampled(rdata)); 
        
    property p_read_before_write;
        not (read && !mem_aarray_exists); // never a read on an non written address
    endproperty : p_read_before_write
    ap_read_before_write : assert property (p_read_before_write);
endmodule : memory_data_integrity_check

class c_xactn;
    rand bit write; // memory write
    rand bit read;  // memory read
    rand int wdata; // data written to memory
    rand bit[31:0] addr;  // memory address 
    constraint addr_range_cst { addr <= 15 ;}
    constraint no_wr_and_rd_cst { !(read && write);}
endclass : c_xactn
module top;
    timeunit 1ns;   timeprecision 100ps;
    bit write; // memory write
    bit read;  // memory read
    int wdata; // data written to memory
    int rdata; // data read from memory
    bit[31:0] addr;  // memory address 
    bit reset_n=1'b1; // active low reset
    bit clk=1'b1;   // clock
    c_xactn c1=new(); 
    memory_data_integrity_check mem_check1 (.*);

    initial forever #50 clk=!clk;

    always_ff @ (posedge clk) begin 
        if(!c1.randomize())  $error("c1 randomization failure"); 
        write <= c1.write; 
        read  <= c1.read; 
        wdata <= c1.wdata;  
        addr  <= c1.addr; 
    end 
endmodule : top 

Ben Cohen
http://www.systemverilog.us/ ben@systemverilog.us
For training, consulting, services: contact Home - My cvcblr

** SVA Handbook 4th Edition, 2016 ISBN 978-1518681448

  1. SVA Package: Dynamic and range delays and repeats SVA: Package for dynamic and range delays and repeats | Verification Academy
  2. Free books: Component Design by Example FREE BOOK: Component Design by Example … A Step-by-Step Process Using VHDL with UART as Vehicle | Verification Academy
    Real Chip Design and Verification Using Verilog and VHDL($3) Amazon.com
  3. Papers:

check here: Edit code - EDA Playground

In reply to juhi_p:

I got to know couple of new things here one is %p which I didn’t know and second thing is usage of const’ in the constraint … thank you … Now I have a question, if you look at the first two lines of the output below in the second line it is trying to read from address 2 but it has never written to that address before… so how is the write before read condition satisfied ??

'{w_addr:3, r_addr:1, w_data:2, w_en:1, r_en:0, r_data:0, cnt:0, flag:0}
'{w_addr:2, r_addr:2, w_data:1, w_en:0, r_en:1, r_data:0, cnt:0, flag:0}

In reply to Subrahmanyam:
for that, additional constraint has to be written where rd_en must be asserted when rd_addr is already written.will think through it.

In reply to juhi_p:

That is why in my above model I used an associative array and the exists to determine in a memory location was written.
I believe that with some additional support logic and a new constraint you can address this issue.
Ben systemverilog.us