UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Adventurer
Adventurer
842 Views
Registered: ‎03-15-2010

Inferring RAMB18/36 with different port sizes AND clocks

Hi,

I'm starting a new thread after reading this Inferring RAMB18/36 with different port sizes topic. Because it was marked as solved, I've decided to start a new one.)

 

Recently I've tried to improve our BRAM macro (which we've already used in some designs, mainly used for buffering data from one clk-domain to another) to support different port sizes. At the moment it looks like this:

 

type my_ram_type is array (2**ADDR_WIDTH-1 downto 0) of std_logic_vector (DATA_WIDTH-1 downto 0);
signal my_ram: my_ram_type := (others => (others => '0'));

attribute ram_style : string;
attribute ram_style of my_ram : signal is "block";

BEGIN

process (clka)
begin
   if (clka'event and clka = '1') then
      if (ena = '1') then
         if (wea = '1') then
            my_ram(conv_integer(addra)) <= dina;
         end if;
      end if;
   end if;
end process;

process (clkb)
begin
   if (clkb'event and clkb = '1') then
      if (enb = '1') then
         doutb <= my_ram(conv_integer(addrb));
      end if;
   end if;
end process;

 

As a very first try without longer research I've tried something like this to support different sizes on A and B.

 

signal my_ram : std_logic_vector((DATA_WIDTH_A*(2**ADDR_WIDTH_A)-1) downto 0) := (others => '0');

type my_ram_a_type is array (2**ADDR_WIDTH_A-1 downto 0) of std_logic_vector(DATA_WIDTH_A-1 downto 0);
signal my_ram_a : my_ram_a_type := (others => (others => '0'));

type my_ram_b_type is array (2**ADDR_WIDTH_B-1 downto 0) of std_logic_vector(DATA_WIDTH_B-1 downto 0);
signal my_ram_b : my_ram_b_type := (others => (others => '0'));

attribute ram_style : string;
attribute ram_style of my_ram : signal is "block";

BEGIN

process (clka)
begin
   if (clka'event and clka = '1') then
      if (ena = '1') then
         if (wea = '1') then
            my_ram_a(conv_integer(addra)) <= dina;
         end if;
      end if;
   end if;
end process;

process (clkb)
begin
   if (clkb'event and clkb = '1') then
      if (enb = '1') then
         doutb <= my_ram_b(conv_integer(addrb));
      end if;
   end if;
end process;

merge_a1: for i in 0 to (2**ADDR_WIDTH_A)-1 generate
    merge_a2: for j in 0 to DATA_WIDTH_A-1 generate
        my_ram(DATA_WIDTH_A*i+j) <= my_ram_a(i)(j);
    end generate merge_a2;
end generate merge_a1;

merge_b1: for i in 0 to (2**ADDR_WIDTH_B)-1 generate
    merge_b2: for j in 0 to DATA_WIDTH_B-1 generate
        my_ram_b(i)(j) <= my_ram(DATA_WIDTH_B*i+j);
    end generate merge_b2;
end generate merge_b1;

 

Simulation looks good, but the Vivado Synthesis complained, that it won't be mapped to BRAM because its design is "too sparse". After short research, I've came up to the thread above and the inferring guidelines in UG901 (p130f). The drawback of the approach in the guideline is, that the width of the ports cannot be changed for all matters, because there are different versions for "read wider than write" and vice versa.

 

So my questions: What is the synthesis tool complaining about my first approach? Is there a proven template, which truly supports different clocks and port widths? Thank you & regards, Harald

0 Kudos
5 Replies
Scholar richardhead
Scholar
826 Views
Registered: ‎08-01-2012

Re: Inferring RAMB18/36 with different port sizes AND clocks

Why have you got 3 ram signals? this is probably whats throwing the synth off. All examples only ever show a single ram.

 

I also get the same errors as you when I try the Altera/Intel HDL style for mixed width ram (which is far more elegant than the Xilinx version, as usual):

 

library ieee;
use ieee.std_logic_1164.all;

package ram_types is
  type word_t is array (0 to 3) of std_logic_vector(7 downto 0);
  type ram_t is array (0 to 255) of word_t;
end ram_types;

library ieee;
use ieee.std_logic_1164.all;
library work;
use work.ram_types.all;

entity mixed_width_ram is
 port (
   we, clk  : in std_logic;
   waddr    : in integer range 0 to 255;
   wdata    : in word_t;
   raddr    : in integer range 0 to 1023;
   q        : out std_logic_vector(7 downto 0));
end mixed_width_ram;

architecture rtl of mixed_width_ram is
  signal ram : ram_t;
  signal q_r : std_logic_vector(q'range);
begin -- rtl

   process(clk, we)
   begin
      if(rising_edge(clk)) then
        if(we = '1') then
          ram(waddr) <= wdata;
        end if;
      
        q_r <= ram(raddr / 4 )(raddr mod 4);
        q   <= q_r;
      end if;
   end process;
end rtl;
0 Kudos
Adventurer
Adventurer
811 Views
Registered: ‎03-15-2010

Re: Inferring RAMB18/36 with different port sizes AND clocks


@richardhead wrote:

Why have you got 3 ram signals? this is probably whats throwing the synth off. All examples only ever show a single ram.


Thank you Richard. Hm, I didn't know how to wrote it with one RAM signal only. I'll used the two additional signals only to access them 2-dimensional with other port widths. And in the merge-statements, the ram-signals are assigned to each other - so I thought that the synth will only implement one of them anyway. Probably this is stupid, but as I said, I didn't could think of another way doing it. I'll think about your addressing way used here, maybe I can use this.

q_r <= ram(raddr / 4 )(raddr mod 4);

Btw: Your example uses the same clock for both ports. In my use-case I need independent clocks on both sides. But maybe this can be splitted in two processes with different clocks.

0 Kudos
Scholar richardhead
Scholar
808 Views
Registered: ‎08-01-2012

Re: Inferring RAMB18/36 with different port sizes AND clocks

My example is the altera one, so dont try it in Vivado - it doesnt work (with 2017.2 anyway)

For dual clock, simply move the read into a 2nd process using the other clock.
0 Kudos
Adventurer
Adventurer
804 Views
Registered: ‎03-15-2010

Re: Inferring RAMB18/36 with different port sizes AND clocks

Ok, so that brings me back again to the BRAM-core-generator from Vivado :-( This way it is possible to find a suitable solution in no-time. But actually, I don't like it much because of many reasons: code-reuse, readability, family-dependencies, .....

0 Kudos
Scholar richardhead
Scholar
798 Views
Registered: ‎08-01-2012

Re: Inferring RAMB18/36 with different port sizes AND clocks

If it helps, Ultrascale has the XPM library, that contains parameterisable rams you can use in Code, without having to use the really annoying coregen.

0 Kudos