- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
the dual RAM: Write port:128×16-bits reading port: 2048×1-bit I pefer HDL design input such VHDL or Verilog, please DO NOT tell me to instantiate this dual RAM by megawizard:) Thanks a lot!Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Altera altsyncram MegaFunction is actually the low level primitive used by Quartus, when inferring RAM from HDL code, you may want to check the physical mapping of your designs. It can be also instantiated in HDL directly, which is, as far I'm aware of, the only way to create dual port RAM with different width. At least, I'm usually going this way. As far as I understand, you don't want to hear about.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ive had this very problem myself:
Solution: Either use the megawizard or include the altera_mf library and instantiate an altsyncram manually. Unfortunately this is no way to infer an altsyncram with different sized in and out ports, and had it confirmed from altera themselves. I have raised it as an enhancement request, so if enough people ask for it, maybe it will appear one day. As a point of note: Xilinx, synopsis and Mentor tools cant do it either.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I must admit, that I don't even see how a dual port RAM with different port widths could be declared unequivocally in VHDL. Do you have a suggestion?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Something like this? (this infers dual port ram fine when addr_a/b and data_a/q_b sizes match) :
entity test_build is
port (
clka : in std_logic;
clkb : in std_logic;
addr_a : in natural range 0 to 127;
data_a : in std_logic_vector(63 downto 0);
addr_b : in natural range 0 to 511;
q_b : out std_logic_vector(15 downto 0)
);
end entity;
architecture rtl of test_build is
type mem_t is array(0 to 511) of std_logic_vector(15 downto 0);
signal mem : mem_t;
signal addr_b_r : integer;
begin
process(clka)
begin
if rising_edge(clka) then
mem(addr_a*4) <= data_a(15 downto 0);
mem(addr_a*4+1) <= data_a(31 downto 16);
mem(addr_a*4+2) <= data_a(47 downto 32);
mem(addr_a*4+3) <= data_a(63 downto 48);
end if;
end process;
process(clkb)
begin
if rising_edge(clkb) then
addr_b_r <= addr_b;
end if;
end process;
q_b <= mem(addr_b_r);
--alt_ram : altsyncram
--generic map (
-- Width_a => 64,
-- width_b => 16,
--
-- widthad_a => 7,
-- widthad_b => 9
--
--)
--port map (
--
-- clock0 => clka,
-- clock1 => clkb,
--
-- data_a => data_a,
-- address_a => std_logic_vector(to_unsigned(addr_a, 7) ),
--
-- address_b => std_logic_vector(to_unsigned(addr_b, 9) ),
-- q_b => q_b
--
--);
end architecture rtl;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am not sure if this would work
type mem_t is array(0 to 511) of std_logic_vector(15 downto 0); signal mem : mem_t; Won't this still infer a memory that is 16 bits wide on both ports? I guess the inference rules for memories are ambiguous and tool dependent. The "not to be named" mega-function wizard seems the safest way;)- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This is the point, you cannot currently infer a memory with missmatched in/out port sizes. The code was one suggested way it "might" be done.
The synthesiser would have to look at the behaviour around the code rather than the declarations, to infer the memory correctly.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for elaborating the idea. Thinking about similar options, I see a problem that it would blow up the rules of "how to infer RAM from HDL" considerably. They are partly difficult yet, e.g. regarding the registered/unregistered features of internal RAM and handling of simultanous write accesses. Thus I think, it may an acceptable idea to leave it as is, requiring explicite altsyncram instantiation for the complex cases.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Example from application note 226:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_signed.all;
ENTITY dualport_ram IS
PORT (
data_out : OUT STD_LOGIC_VECTOR (63 DOWNTO 0);
data_in : IN STD_LOGIC_VECTOR (63 DOWNTO 0);
wr_addr : IN STD_LOGIC_VECTOR (6 DOWNTO 0);
rd_addr : IN STD_LOGIC_VECTOR (6 DOWNTO 0);
we : IN STD_LOGIC;
clk : IN STD_LOGIC);
END dualport_ram;
ARCHITECTURE ram_infer OF dualport_ram IS
TYPE Mem_Type IS ARRAY (127 DOWNTO 0) OF STD_LOGIC_VECTOR (63 DOWNTO 0);
SIGNAL mem: Mem_Type;
SIGNAL addr_reg: STD_LOGIC_VECTOR (6 DOWNTO 0);
BEGIN
data_out <= mem (CONV_INTEGER(rd_addr));
PROCESS (clk, we, data_in)
BEGIN
IF (clk=’1’ AND clk’EVENT) THEN
IF (we=’1’) THEN
mem(CONV_INTEGER(wr_addr)) <= data_in;
END IF;
END IF;
END PROCESS;
END ram_infer;
For your second port, use an address translation and bit selection from 16 to 64 in an upper module. Edit: copy and past issue: thanks to Tricky for the feedback.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That will never work - the mem_type stores 8 bit values, while data in is 64 bits wide.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- For your second port, use an address translation and bit selection from 16 to 64 in an upper module. --- Quote End --- Thats fine, but its not as elegant as the direct instantiation - it will add muxes on the output. Its not terrible, just not as compact.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quartus II 8.0 fail to infer following code to RAM:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity test_build is port ( clka : in std_logic; clkb : in std_logic; addr_a : in std_logic_vector(6 downto 0);--in natural range 0 to 127; data_a : in std_logic_vector(63 downto 0); addr_b : in std_logic_vector(8 downto 0 );--in natural range 0 to 511; q_b : out std_logic_vector(15 downto 0) ); end entity; architecture rtl of test_build is -- Build a 2-D array type for the RAM type word_t is array (2**9-1 downto 0) of std_logic_vector (15 downto 0); shared variable mem : word_t; signal addr_b_r : std_logic_vector(8 downto 0);--integer; begin process(clka) begin if rising_edge(clka) then --mem(conv_integer(addr_a)) := data_a; mem(conv_integer(addr_a) * 4+0) := data_a(15 downto 0); mem(conv_integer(addr_a) * 4+1) := data_a(31 downto 16); mem(conv_integer(addr_a) * 4+2) := data_a(47 downto 32); mem(conv_integer(addr_a) * 4+3) := data_a(63 downto 48); end if; end process; --rd_addr_t <= std_logic_vector(to_unsigned(addr_b, 9) ); process(clkb) begin if rising_edge(clkb) then addr_b_r <= addr_b; q_b <= mem(conv_integer(addr_b_r)); -- case (addr_b_r(1 downto 0)) is -- when "11" => -- q_b <= q_t(63 downto 48); -- when "10" => -- q_b <= q_t(47 downto 32); -- when "01" => -- q_b <= q_t(31 downto 16); -- when "00" => -- q_b <= q_t(15 downto 0); -- when others => -- q_b <= x"0000"; -- end case; end if; end process; --q_b <= mem(addr_b_r); --alt_ram : altsyncram --generic map ( -- Width_a => 64, -- width_b => 16, -- -- widthad_a => 7, -- widthad_b => 9 -- --) --port map ( -- -- clock0 => clka, -- clock1 => clkb, -- -- data_a => data_a, -- address_a => std_logic_vector(to_unsigned(addr_a, 7) ), -- -- address_b => std_logic_vector(to_unsigned(addr_b, 9) ), -- q_b => q_b -- --); end architecture rtl; As Tricky posted, it's not a bad idea to add muxes on the output, like the quoted part using "case" above. U need to asure the width of R/W ports is same, as well as the address.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I don't see a purpose of using a shared variable for RAM inference, also I doubt if it's supported in this place by Quartus? It's never mentioned in the Quartus Handbook.
In the above code, you are trying to write to four consecutive RAM addresses simultanously. This can't work in a single clock cycle.if rising_edge(clka) then
--mem(conv_integer(addr_a)) := data_a;
mem(conv_integer(addr_a) * 4+0) := data_a(15 downto 0);
mem(conv_integer(addr_a) * 4+1) := data_a(31 downto 16);
mem(conv_integer(addr_a) * 4+2) := data_a(47 downto 32);
mem(conv_integer(addr_a) * 4+3) := data_a(63 downto 48);
end if;
If you want to emulate different port width's with equal port width RAM, you must do it the other way around: Define the RAM width the larger width and use individual byteenable signals, when writing to the smaller port is intended
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Shared variables should not be synthesizable, but it appears they are in Quartus!.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quartus II 8.0 DO infer following code to dual port RAM:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; --use ieee.numeric_std.all; entity test_build is generic ( DATA_WIDTH : natural := 64; ADDR_WIDTH : natural := 7 ); port ( clka : in std_logic; clkb : in std_logic; wren : in std_logic := '1'; addr_a : in std_logic_vector(ADDR_WIDTH-1 downto 0);--in natural range 0 to 127; data_a : in std_logic_vector(DATA_WIDTH-1 downto 0); rden : in std_logic := '1'; addr_b : in std_logic_vector(ADDR_WIDTH-1 downto 0 );--in natural range 0 to 511; q_b : out std_logic_vector(15 downto 0) ); end entity; architecture rtl of test_build is --type mem_t is array(0 to 511) of std_logic_vector(15 downto 0); -- Build a 2-D array type for the RAM type word_t is array (2**ADDR_WIDTH-1 downto 0) of std_logic_vector (DATA_WIDTH-1 downto 0); shared variable mem : word_t; --attribute ramstyle : string; --attribute ramstyle of mem : variable is "no_rw_check"; signal addr_b_r : std_logic_vector(ADDR_WIDTH-1 downto 0);--integer; signal q_t : std_logic_vector(DATA_WIDTH-1 downto 0); begin process(clka) begin if rising_edge(clka) then if(wren = '1') then mem(conv_integer(addr_a)) := data_a; -- mem(conv_integer(addr_a) * 4+0) := data_a(15 downto 0); -- mem(conv_integer(addr_a) * 4+1) := data_a(31 downto 16); -- mem(conv_integer(addr_a) * 4+2) := data_a(47 downto 32); -- mem(conv_integer(addr_a) * 4+3) := data_a(63 downto 48); end if; end if; end process; process(clkb) begin if rising_edge(clkb) then if(rden = '1') then addr_b_r <= addr_b; q_t <= mem(conv_integer(addr_b_r)); --q_b <= q_t(to_unsigned(addr_b(1 downto 0))); --q_b <= mem(conv_integer(addr_b_r)); end if; case (addr_b_r(1 downto 0)) is when "11" => q_b <= q_t(63 downto 48); when "10" => q_b <= q_t(47 downto 32); when "01" => q_b <= q_t(31 downto 16); when "00" => q_b <= q_t(15 downto 0); when others => q_b <= x"0000"; end case; end if; end process; --q_b <= mem(addr_b_r); --alt_ram : altsyncram --generic map ( -- Width_a => 64, -- width_b => 16, -- -- widthad_a => 7, -- widthad_b => 9 -- --) --port map ( -- -- clock0 => clka, -- clock1 => clkb, -- -- data_a => data_a, -- address_a => std_logic_vector(to_unsigned(addr_a, 7) ), -- -- address_b => std_logic_vector(to_unsigned(addr_b, 9) ), -- q_b => q_b -- --); end architecture rtl; Well the data width of R/W port must be the same the R/W enable signals are necessary.Even using "Shared variables", it's easy synthesized by Quartus II 8.0! Unfortuntly, I can not use"ramstyle " attribution with "no_rw_check" when using "Shared variables", because "ramstyle " ONLY for "signal" NOT for "variables" as follow code frage: signal mem : word_t; attribute ramstyle : string; attribute ramstyle of mem : signal is "no_rw_check";
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page