How to write into 12 addresses at the same cycle in vivado and still be recognized as BRAM

100 views Asked by At

This is the original code,It is synthesized as a BRAM

module RAM_IMAGINARY (clk, we, en, addr, di, dout);
input clk;
input we;
input en;
input [7:0] addr;
input [15:0] di;
output [15:0] dout;
logic [15:0] RAM_IMAGINARY [167:0];
logic [15:0] dout;
always @(posedge clk)
begin
if (en)begin
if (we) begin
  RAM_IMAGINARY[addr] <= di;
  dout <= di;
end
else
dout <= RAM_IMAGINARY[addr];
end
end
endmodule      

I want to write a whole column in my memory at a cycle, Imagine that this RAM is 12x14.

I also tried to instantiate just one of the 12 in each module like so ,but they weren't recognized as BRAMs due to the small width and depth.

       module RAM_IMAGINARY (clk, we, en, addr, di, dout);
       input clk;
       input we;
       input en;
       input [7:0] addr;
       input [15:0] di;
       output [15:0] dout;
       logic [15:0] RAM_IMAGINARY [13:0];
       logic [15:0] dout;
       always @(posedge clk)
       begin
       if (en)begin
       if (we) begin
       RAM_IMAGINARY[addr] <= di;
       dout <= di;
       end
       else
       dout <= RAM_IMAGINARY[addr];
       end
       end
       endmodule
    module Top(clk, we, en, addr, di, dout0,dout1,dout2,dout3,dout4,dout5,dout6,dout7,dout8,dout9,dout10,dout11);
    input clk;
    input we;
    input en;
    input [7:0] addr;
    input [15:0] di;
    output [15:0] dout0,dout1,dout2,dout3,dout4,dout5,dout6,dout7,dout8,dout9,dout10,dout11;
    RAM_IMAGINARY RAM0 (clk, we, en, addr, di, dout0) ;
    RAM_IMAGINARY RAM1 (clk, we, en, addr, di, dout1) ;
    RAM_IMAGINARY RAM2 (clk, we, en, addr, di, dout2) ;
    RAM_IMAGINARY RAM3 (clk, we, en, addr, di, dout3) ;
    RAM_IMAGINARY RAM4 (clk, we, en, addr, di, dout4) ;
    RAM_IMAGINARY RAM5 (clk, we, en, addr, di, dout5) ;
    RAM_IMAGINARY RAM6 (clk, we, en, addr, di, dout6) ;
    RAM_IMAGINARY RAM7 (clk, we, en, addr, di, dout7) ;
    RAM_IMAGINARY RAM8 (clk, we, en, addr, di, dout8) ;
    RAM_IMAGINARY RAM9 (clk, we, en, addr, di, dout9) ;
    RAM_IMAGINARY RAM10 (clk, we, en, addr, di, dout10) ;
    RAM_IMAGINARY RAM11 (clk, we, en, addr, di, dout11) ;
    
        
    endmodule

This is my code which is a typical BRAM code from vivado guide, I want to write into a whole column at once, imagine that it is a 12x14 memory. I tried to instantiate 12 rams and write into each one at the same cycle ,but due to the small depth and width, it wasn't recognized as a BRAM. Help

I tried writing into 12 addresses at same cycle

1

There are 1 answers

1
Mikef On

I added the (* ram_style = “block” *) attribute to the RAM_IMAGINARY model, now it is recognized as BRAM during Vivado synthesis as a stand-alone module and as a instance in the Top module.

The tool needed a hint re the intent to infer BRAM.

Sometimes the tool needs the attribute to infer what you want (BRAM, DSP Block etc); sometimes not. Its tricky to know why. I think the details involve the tool optimizing recourses given the circumstances. Vivado seems to want to use distributed RAM aka LUT RAM if it can. If you use the true dual port model then the tool can't use LUT RAM and I think the bias switches to BRAM. If you model structures not supported as RAM, then the tool will use logic fabric.

Here is the ram model with the attributed added

module RAM_IMAGINARY (clk, we, en, addr, di, dout);
  input clk;
  input we;
  input en;
  input [7:0] addr;
  input [15:0] di;
  output [15:0] dout;

// this is the only line that changed
(* ram_style = “block” *) logic [15:0] RAM_IMAGINARY [13:0];

always @(posedge clk)
begin
  if (en)begin
  if (we) begin
  RAM_IMAGINARY[addr] <= di;
  dout <= di;
  end
  else
  dout <= RAM_IMAGINARY[addr];
  end
end

endmodule

I used your Top module with no changes

    module Top(clk, we, en, addr, di, dout0,dout1,dout2,dout3,dout4,dout5,dout6,dout7,dout8,dout9,dout10,dout11);     

    input clk;
    input we;
    input en;
    input [7:0] addr;
    input [15:0] di;
    output [15:0] dout0,dout1,dout2,dout3,dout4,dout5,dout6,dout7,dout8,dout9,dout10,dout11;
    RAM_IMAGINARY RAM0 (clk, we, en, addr, di, dout0) ;
    RAM_IMAGINARY RAM1 (clk, we, en, addr, di, dout1) ;
    RAM_IMAGINARY RAM2 (clk, we, en, addr, di, dout2) ;
    RAM_IMAGINARY RAM3 (clk, we, en, addr, di, dout3) ;
    RAM_IMAGINARY RAM4 (clk, we, en, addr, di, dout4) ;
    RAM_IMAGINARY RAM5 (clk, we, en, addr, di, dout5) ;
    RAM_IMAGINARY RAM6 (clk, we, en, addr, di, dout6) ;
    RAM_IMAGINARY RAM7 (clk, we, en, addr, di, dout7) ;
    RAM_IMAGINARY RAM8 (clk, we, en, addr, di, dout8) ;
    RAM_IMAGINARY RAM9 (clk, we, en, addr, di, dout9) ;
    RAM_IMAGINARY RAM10 (clk, we, en, addr, di, dout10) ;
    RAM_IMAGINARY RAM11 (clk, we, en, addr, di, dout11) ;
    
    endmodule

See UG901 P49 for more on the ram_style attribute.

(* ram_style = “block” *) causes compile errors for me in the big four commercial simulators available on EDA Playground; its a Xilinx/Vivado attribute only.


The post synthesis schematic and utilization report summary look like this, after adding the attribute

enter image description here


As you can see Vivado inferred a single memory. The Top model RTL is structurally modeled as 12 memories, however the same address and data are connected to each memory. The same data would be read from all 12. Because of this, Vivado optimizes and infers one memory connected to all 12 output busses. If you modify the code to use 12 unique addresses then the tool will infer 12 memories. You could modify the design to use 12 unique addresses on the read side and the tool should infer 12 memories.

Here is the 12 memories version\w a parameter to set the number of memories.

module Top
  #(parameter NUM = 12)
  (
  input logic         clk,
  input logic         we,
  input logic         en,
  input logic [7:0]   addr [NUM - 1 : 0],
  input logic [15:0]  di,
  output logic [15:0] dout [NUM - 1 : 0]
  );

   generate genvar i;
      for(i =0; i < NUM; i++)
        RAM_IMAGINARY RAM0
        (
        .clk(clk), .we(we), .en(en), .addr(addr[i]), .di(di), .dout(dout[i])
        );
   endgenerate
   
endmodule

And the synthesis result

enter image description here