Inferring RAM

The following sections provide example for simple and true dual-port inferencing.

Simple Dual-Port Memory Examples

The following example infers a 512 x 8 RAM. Because both writes and reads are performed with a blocking statement and the write occurs before the read in the always block, the software infers a simple dual ported RAM in WRITE_FIRST mode.

Figure 1. Simple Dual-Port RAM in WRITE_FIRST Mode
module ram512x8_sp(wdata, addr, clk, we, rdata);
   parameter AWIDTH = 9;
   parameter DWIDTH = 8;
   localparam DEPTH = 1 << AWIDTH;
   localparam MAX_DATA = (1<<DWIDTH)-1;
   input [DWIDTH-1:0] wdata;
   input [AWIDTH-1:0] addr;
   input  clk, we;
   output reg [DWIDTH-1:0] rdata;

   reg [DWIDTH-1:0] mem [DEPTH-1:0];

   // Blocking Statement and order indicates write before read
   always@(posedge clk) begin
      if (we) begin
          mem[addr] = wdata;
      end
      rdata = mem[addr];
   end
endmodule

If the read and write clocks are different, the software configures the memory primitive as READ_UNKNOWN. That is, if you read and write to the same address at the same time, the read data is indeterministic.

Figure 2. Simple Dual-Port RAM in READ_UNKNOWN Mode
module ram512x8_sp(wdata, addr, rclk, re, wclk, we, rdata);
   parameter AWIDTH = 9;
   parameter DWIDTH = 8;
   localparam DEPTH = 1 << AWIDTH;
   localparam MAX_DATA = (1<<DWIDTH)-1;
   input [DWIDTH-1:0] wdata;
   input [AWIDTH-1:0] addr;
   input 			  rclk, re, wclk, we;
   output reg [DWIDTH-1:0] rdata;

   reg [DWIDTH-1:0]  mem [DEPTH-1:0];

   // different read and write clock, forces READ_UNKNOWN mode
   always@(posedge wclk) begin
      if (we) begin
          mem[addr] = wdata;
      end
   end
   always@(posedge rclk) begin
      if (re) begin
          rdata = mem[addr];
      end
   end
endmodule
Figure 3. Simple Dual-Port RAM with Byte Enable (Verilog HDL) (Titanium)
// 16-bit wide, 512 depth, byte-enabled
// fits into 1 Titanium 10K blockram
module ram10_be1 #(
        parameter integer wrAddressWidth = 9,   // 512 depth
        parameter integer wrDataWidth = 16,     // 16-bit wide
        parameter integer wrMaskWidth = 2,
        parameter integer rdAddressWidth = 9,
        parameter integer rdDataWidth  = 16
    )(
        input wr_clk,
        input wr_en,
        input [wrMaskWidth-1:0] wr_mask,
        input [wrAddressWidth-1:0] wr_addr,
        input [wrDataWidth-1:0] wr_data,
        input rd_clk,
        input rd_en,
        input [rdAddressWidth-1:0] rd_addr,
        output [rdDataWidth-1:0] rd_data
    );

    reg [wrDataWidth-1:0] ram_block [(2**wrAddressWidth)-1:0];
    integer i;
    localparam COL_WIDTH = wrDataWidth/wrMaskWidth;
    always @ (posedge wr_clk) begin
        if(wr_en) begin
            for(i=0;i<wrMaskWidth;i=i+1) begin
                if(wr_mask[i]) begin    // byte-enable
                    ram_block[wr_addr][i*COL_WIDTH +: COL_WIDTH] <= wr_data[i*COL_WIDTH +:COL_WIDTH];
                end
            end
        end
    end

    reg [rdDataWidth-1:0] ram_rd_data;
    always @ (posedge rd_clk) begin
        if(rd_en) begin
            ram_rd_data <= ram_block[rd_addr];
        end
    end
    assign rd_data = ram_rd_data;
endmodule
Figure 4. Simple Dual-Port RAM with Byte Enable (VHDL) (Titanium)
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- 512 x 32, with 4 byte-enable signals

entity Memory is
    generic (ADDR_WIDTH: integer := 9); 
    Port ( DBOut : out  STD_LOGIC_VECTOR (31 downto 0);
           DBIn : in  STD_LOGIC_VECTOR (31 downto 0);
           AdrBus : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
           ENA : in  STD_LOGIC;
           WREN : in  STD_LOGIC_VECTOR (3 downto 0);
           CLK : in  STD_LOGIC
         );
end Memory;

architecture Behavioral of Memory is

constant SIZE : natural := 2**ADDR_WIDTH;
type tRam is array (0 to SIZE-1) of STD_LOGIC_VECTOR (31 downto 0);
subtype tWord is std_logic_vector(31 downto 0);

signal ram : tRam;
signal DOA,DIA : tWord;
signal WEA : STD_LOGIC_VECTOR (3 downto 0);

begin

  DIA<=DBIn;
  DBOut<=DOA;
  WEA<=WREN;

  process(clk)
  variable adr : integer;
  begin
    if rising_edge(clk) then
        if ena = '1' then
           adr :=  to_integer(unsigned(AdrBus));

           for i in 0 to 3 loop
             if WEA(i) = '1' then
                ram(adr)((i+1)*8-1 downto i*8)<= DIA((i+1)*8-1 downto i*8);
             end if;
           end loop;

           DOA <= ram(adr);

         end if;
     end if;

  end process;

end Behavioral;

True Dual-Port Memory Examples

In true dual-port RAM, the two ports have independent read and write functions. Each port supports different write modes. The following example shows how to implement a 512 x 8 RAM block with READ_FIRST write mode for port A and WRITE_FIRST mode for port B.

module ram512x8_tdp_mix (wdataA, addrA, clkA, weA, rdataA, wdataB, addrB, clkB, weB, rdataB);
   parameter AWIDTH = 9;
   parameter DWIDTH = 8;
   localparam DEPTH = 1 << AWIDTH;
   localparam MAX_DATA = (1<<DWIDTH)-1;
   
   input [DWIDTH-1:0] wdataA, wdataB;
   input [AWIDTH-1:0] addrA, addrB;
   input    clkA, weA;
   input    clkB, weB;
   output reg [DWIDTH-1:0] rdataA, rdataB;

   reg [DWIDTH-1:0]   mem [DEPTH-1:0];

   integer   i;
   initial begin
      // The memory is initialized with
      // decreasing values startingfrom MAX_DATA
      for (i=0;i<DEPTH;i=i+1)
         mem[i] = MAX_DATA - i;
   end

   always@(posedge clkA) begin
      // Use blocking assignments to for read-first
      rdataA = mem[addrA];
      if (weA) begin
         mem[addrA] = wdataA;
      end
   end

   always@(posedge clkB) begin
      // Use blocking assignments to force write-first
      if (weB) begin
         mem[addrB] = wdataB;
      end
      rdataB = mem[addrB];
   end
endmodule

Initializing RAM

Initialize the memory content with the Verilog HDL $readmemh or $readmemb routines.

Figure 5. Initializing RAM in Verilog HDL
module ram_256x16 (wdata,  waddr, wclk, we, raddr, rclk, re, rdata);
   localparam addr_width = 8;
   localparam data_width = 16;
   input [data_width-1:0] wdata;
   input [addr_width-1:0] waddr, raddr;
   input    wclk, we;
   input    rclk, re;
   output reg [data_width-1:0] rdata;

   reg [data_width-1:0]   mem [(1<<addr_width)-1:0];

   integer   i;
   initial begin
      // Initialize memory with external file
      $readmemh("ram256x16b.inithex", mem);
   end

   always@(posedge wclk) begin
       if (we)
	    mem[waddr] <= wdata;
       end
   always@(posedge rclk) begin
       if (re)
           rdata <= mem[raddr];
       end
   
endmodule // ram_256x16

The memory file should be simple hexadecimal numbers ($readmemh) or binary numbers ($readmemb) without any comments or prefixes.

Figure 6. Example Memory File
FE
FD
FC
FB
FA
F9
F8
F7
F6
F5
...

Inferring Output Registers

Synthesis packs registers that immediately follow the read data into the output registers of the BRAM if the control logic is compatible:

  • The read clock is the same as the register clock signal.
  • Enables:
    • Trion FPGAs—The register must always be enabled (no explicit clock enable control).
    • Titanium FPGAs—The register's clock enable must be the same as the BRAM's read enable signal.
  • Resets:
    • Trion FPGAs—The register cannot have a reset signal.
    • Titanium FPGAs—If the read port has a reset signal, and if the register has a reset signal, they must match. Additionally, the Titanium output register only supports asynchronous reset logic.

Address Enable (Titanium)

The Titanium BRAM supports an address enable feature. However, synthesis does not infer these signals;for inferred BRAM these signals are always tied high. To use the address enable, instantiate the primitive (EFX_RAM10 and EFX_DPRAM10),

Resetting RAM (Titanium)

Titanium RAM supports a reset option on the RAM output and output register.

Figure 7. RAM Output with Asynchronous Reset
module ram10_arst (wdata, waddr, wclk, wclke, rclk, we, re, raddr, rdata, rst);
   parameter AWIDTH = 11;   // 2048 depth
   parameter DWIDTH = 4;    // 4-bit wide
   localparam DEPTH = 1 << AWIDTH;
   input [DWIDTH-1:0] wdata;
   input [AWIDTH-1:0] waddr, raddr;
   input              wclk, wclke, we, rclk, re, rst;
   output reg [DWIDTH-1:0] rdata;

   reg [DWIDTH-1:0]        mem [DEPTH-1:0];

   always@(posedge wclk) begin
      if (wclke) begin
        if (we)
            mem[waddr] <= wdata;
      end
   end
   always@(posedge rclk or posedge rst) begin
      if (rst)
        rdata <= 0;
      else if (re)
        rdata <= mem[raddr];
    end
endmodule