Verilog Coding Tips and Tricks: Verilog code for a Dual Port RAM with Testbench

Thursday, November 2, 2017

Verilog code for a Dual Port RAM with Testbench

There are many types RAM. These differ in terms of the number of ports, synchronous or asynchronous modes of operation etc.

Here in this post, I have written the Verilog code for a simple Dual port RAM, with two ports 0 and 1. The writing is allowed to only one port, on the positive edge the clock. The reading is done from both the ports asynchronously, that means we don't have to wait for the clock signal to read from the memory.

I have fixed the RAM size as 16*8 bits, meaning 16 elements of 8 bits each. These specifications can be changed easily by altering few lines in the code.

Verilog code:

module dual_port_ram 
    (   input clk, //clock
        input wr_en,    //write enable for port 0
        input [7:0] data_in,    //Input data to port 0.
        input [3:0] addr_in_0,  //address for port 0
        input [3:0] addr_in_1,  //address for port 1
        input port_en_0,    //enable port 0.
        input port_en_1,    //enable port 1.
        output [7:0] data_out_0,    //output data from port 0.
        output [7:0] data_out_1 //output data from port 1.
    );

//memory declaration.
reg [7:0] ram[0:15];

//writing to the RAM
always@(posedge clk)
begin
    if(port_en_0 == 1 && wr_en == 1)    //check enable signal and if write enable is ON
        ram[addr_in_0] <= data_in;
end

//always reading from the ram, irrespective of clock.
assign data_out_0 = port_en_0 ? ram[addr_in_0] : 'dZ;   
assign data_out_1 = port_en_1 ? ram[addr_in_1] : 'dZ;   

endmodule 

Testbench code:

module tb;

    // Inputs
    reg clk;
    reg wr_en;
    reg [7:0] data_in;
    reg [3:0] addr_in_0;
    reg [3:0] addr_in_1;
    reg port_en_0;
    reg port_en_1;

    // Outputs
    wire [7:0] data_out_0;
    wire [7:0] data_out_1;
    
    integer i;

    // Instantiate the Unit Under Test (UUT)
    dual_port_ram uut (
        .clk(clk), 
        .wr_en(wr_en), 
        .data_in(data_in), 
        .addr_in_0(addr_in_0), 
        .addr_in_1(addr_in_1), 
        .port_en_0(port_en_0), 
        .port_en_1(port_en_1), 
        .data_out_0(data_out_0), 
        .data_out_1(data_out_1)
    );
    
    always
        #5 clk = ~clk;

    initial begin
        // Initialize Inputs
        clk = 1;
        addr_in_1 = 0;
        port_en_0 = 0;
        port_en_1 = 0;
        wr_en = 0;
        data_in = 0;
        addr_in_0 = 0;  
        #20;
        //Write all the locations of RAM
        port_en_0 = 1;  
        wr_en = 1;
      for(i=1; i <= 16; i = i + 1) begin
            data_in = i;
            addr_in_0 = i-1;
            #10;
        end
        wr_en = 0;
        port_en_0 = 0;  
        //Read from port 1, all the locations of RAM.
        port_en_1 = 1;  
        for(i=1; i <= 16; i = i + 1) begin
            addr_in_1 = i-1;
            #10;
        end
        port_en_1 = 0;
    end
      
endmodule

The code synthesised and simulated using Xilinx ISE 14.6 tool.

Simulation waveform:


Synthesis:

The synthesis was done for a Virtex 6 device. Note that, for this particular device the inbuilt BRAM's were not used to implement this RAM because of asynchronous read operations. Making it synchronous might use the available BRAM's.

I have written a VHDL version of the same RAM in my other blog. See VHDL code for Dual port RAM.


No comments:

Post a Comment