[ Verilog FAQ  |  Tips  |  Online Books Papers  |  Free Stuff |   Tools  | Jobs | What's New  ]

Technical Tidbits

Introduction

This contribution came from a discussion on the comp.lang.verilog bulletin board which was initiated by Farhad Sunavala concerning RAM modelling. Thanks go to t he several people who contributed to the discussion and to Farhad, for summarizing the discussion here.

The question was, "how do you model a RAM in Verilog?" Assume the following:

// r_wb -> read/writebar
// addr -> address inputs
// d -> data inputs
// q -> RAM output

Solution 1:

Farhad's original version was:
module mem (r_wb,addr,d,q);
input r_wb;
input [7:0] addr;
input [7:0 ] d;
ouput [7:0] q;
reg [7:0] q;
reg [7:0] mem_bank [0:255];
always @(r_wb)
     if (r_wb) #15 q=mem_bank[addr];
     else #15 mem_bank[addr]=d;
always @(addr)
     if (r_wb) #15 q=mem_bank[addr];
     else #15 mem_bank[addr]=d;
always @(d)
     if (r_wb) #15 q=mem_bank[addr];
     else # 15 mem_bank[addr]=d;
endmodule

 

This works fine since the RAM inputs & outputs are separate. However, consider t he case of a RAM where the input data and output data are one inout port:

module mema (r_wb,addr,d_q);
input r_wb;
input [7:0] addr;
inout [7:0] d_q;

Solution 1 does not work for this case since the bidirectional data bus cannot b e declared as a register in the module.

Solution 2:

Gary Cook (BNR Europe) suggested the following:
module mema (r_wb,addr,d_q);
input r_wb; input [7:0] addr;
inout [7:0] d_q;
reg [7:0] data [0:255];
assign d_q = (r_wb) ? data[addr] : 8'hz ;
always @(r_wb)
    if (!r_wb) data[addr] = d_q ;
always @(addr)
    if (!r_wb) data[addr] = d_q ;
endmodule
 

The assign puts the stored 'data' onto the d_q bus when r_wb is high, ie indicat ing a read. When d_q is low , the assign tri-states the d_q which can be driven by the driving device.

[Ed. note: This solution has several potential gotchas. What does this model do if addr and r_wb change at the same time?]

Solution 3:

David McGhee (University of Alabama) contributed the following:

Here's an example of how I do memories - tribuf8 is a module that contains 8 tri -state buffers:

`define MEMORIES
`ifdef REGISTERS
`else
    `include "registers.v"
`endif
`ifdef DELAYPERGATE
`else
    `define DELAYPERGATE 10
`endif
module memory64Kx8(data,address,wr,rd);
parameter delay = `DELAYPERGATE;
inout [ 7:0] data;
input [15:0] address;
input wr,rd;
// Modeling memory using 4 CMOS
// transistors per bit...yeah right..
reg [7:0] memoryspace [65535:0];
wire [7:0] localdataout;
// The value at the current address is
// always at the input of the buffer
assign localdataout = memoryspace[address];
// A rising edge of the write pulse
// actually puts the data into memory
always @(negedge wr)
   #delay memoryspace[address] <= data;
// Write must be low to prevent
// conflicts during a read!
and #delay u1(output enable,rd,~wr);
// If a read occurs then just
// enable the output wires onto
// the bus.
tribuf 8 #delay outbuf(data,localdataout, outputenable);
endmodule

[Ed. note: Check out the use of a non-blocking assign in the write to memoryspac e. Is this correct? Hint: the delay should be an intra-assignment delay. Then, t he assignment can be either blocking or non-blocking.]

Conclusion

Modelling a RAM is a common thing to do in Verilog, and there are a variety of ways to do it. Most all are some variation on using a bidirectional port with a conditional driver, either a conditional continuous assignment or a tri-state buffer.
 

[ Verilog FAQ  |  Tips  |  Online Books Papers  |  Free Stuff |   Tools  | Jobs | What's New  ]

Copyright Rajesh Bawankule  1997-2013