Forum Discussion
Proper way to infer Quad Port Ram?
I generated the Quad Port Template in Quartus 2025.3. I then wrapped it into a wrapper and finally into my design.
This is then instantiated with the following generics:
Info(19337): VHDL info at quad_port_ram.vhd(10): executing entity "quad_port_ram(data_width=578,addr_width=7)" with architecture "rtl"
Info(19337): VHDL info at quad_port_ram.vhd(10): executing entity "quad_port_ram(data_width=578,addr_width=8)" with architecture "rtl"
Info(19337): VHDL info at quad_port_ram.vhd(10): executing entity "quad_port_ram(data_width=578,addr_width=9)" with architecture "rtl"
Info(19337): VHDL info at quad_port_ram.vhd(10): executing entity "quad_port_ram(data_width=578,addr_width=10)" with architecture "rtl"
However I get the following errors:
Warning(276002): Cannot convert all sets of registers into RAM megafunctions when creating nodes; therefore, the resulting number of registers remaining in design can cause longer compilation time or result in insufficient memory to complete Analysis and Synthesis
Is there something that can be done to properly infer quad-port ram in 25.3?
Here's the VHDL generated from the template:
-- Quartus Prime VHDL Template
-- Quad Port RAM with separate read/write addresses and
-- single read/write clock
-- This style of RAM cannot be used on Arria 10,
-- which does not support Quad Port RAM
library ieee;
use ieee.std_logic_1164.all;
entity quad_port_ram is
generic
(
DATA_WIDTH : natural := 2;
ADDR_WIDTH : natural := 6
);
port
(
clk : in std_logic;
read_addr_a : in natural range 0 to 2**ADDR_WIDTH - 1;
read_addr_b : in natural range 0 to 2**ADDR_WIDTH - 1;
write_addr_a : in natural range 0 to 2**ADDR_WIDTH - 1;
write_addr_b : in natural range 0 to 2**ADDR_WIDTH - 1;
data_a : in std_logic_vector((DATA_WIDTH-1) downto 0);
data_b : in std_logic_vector((DATA_WIDTH-1) downto 0);
we_a : in std_logic := '1';
we_b : in std_logic := '1';
q_a : out std_logic_vector((DATA_WIDTH -1) downto 0);
q_b : out std_logic_vector((DATA_WIDTH -1) downto 0)
);
end quad_port_ram;
architecture rtl of quad_port_ram is
-- Build a 2-D array type for the RAM
subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0);
type memory_t is array(2**ADDR_WIDTH-1 downto 0) of word_t;
-- Declare the RAM
shared variable ram : memory_t;
begin
-- Port A
process(clk)
begin
if(rising_edge(clk)) then
if(we_a = '1') then
ram(write_addr_a) := data_a;
end if;
end if;
end process;
process(clk)
begin
if(rising_edge(clk)) then
q_a <= ram(read_addr_a);
end if;
end process;
-- Port B
process(clk)
begin
if(rising_edge(clk)) then
if(we_b = '1') then
ram(write_addr_b) := data_b;
end if;
end if;
end process;
process(clk)
begin
if(rising_edge(clk)) then
q_b <= ram(read_addr_b);
end if;
end process;
end rtl;
5 Replies
- sstrell
Super Contributor
What device are you targeting?
- RichardT_altera
Super Contributor
The template provided in Quartus is the correct way to infer the Quad-Port RAM.
However, as sstrell mentioned, it depends on the device you are using. Quad-Port RAM is supported in Agilex and Stratix 10 devices.
You can verify this by navigating to:IP Catalog → RAM: 4-PORT FPGA IP → Right-click → Details → Supported Device Families
Regards,
Richard Tan - mschiller-nrao
New Contributor
Sorry I didn't make that clear This is an AgileX I-series part.
For now I've gotten it to work with effectively an IP core.. I generated the IP core, and then reverse engineered what it was calling to use the primitive/megafunction directly. (eg altera_syncram). But I hate that solution because for quad port it seems that only the altera_mf_ver (verilog) library even has the altera_syncram model [and for some reason the sim library calls it altera_syncram_derived instead of altera_syncram?!?!]
I'm a VHDL shop, so any verilog in my code base pisses me off because I can't use opensource simulators. [I do have a single seat of rivierapro... which I'm using to simulate this solution] - mschiller-nrao
New Contributor
As another point of information. I tried synthesizing the inferred quad_port_ram outside my design. For reasonable size blocks eg 2048 deep x 32 wide the file inferred properly. But I appear to start having problems as the block gets wider. So I'm assuming this is some kind of synthesis bug where the wide ram (like 500x2048!) triggers the bug. But it does work with the altera_syncram (basically the IP) solution....
- RichardT_altera
Super Contributor
What is the problem you are facing? Which parameter setting caused the issue? Please provide more details.
Additionally, what specific memory configuration setting do you plan to use?