Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
Announcements
Intel Support hours are Monday-Fridays, 8am-5pm PST, except Holidays. Thanks to our community members who provide support during our down time or before we get to your questions. We appreciate you!

Need Forum Guidance? Click here
Search our FPGA Knowledge Articles here.
15550 Discussions

Inferring RAM with read enable

BLee15
New Contributor I
909 Views

To learn VHDL, I'm implementing my own custom CPU with VHDL.

 

I'm implementing memory-mapped IO, which access traditional RAM and various I/O peripherals as same manner from the viewpoint of user code.

 

As multiple peripherals will share data bus, I implemented read enable signal and use hi-Z state like this:

 

 

 

library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.utility.all; entity onchip_ram is generic ( addr_width: integer; data_width: integer ); port ( clock: in std_logic; addr: in std_logic_vector(addr_width-1 downto 0); rq: out std_logic_vector(data_width-1 downto 0); wq: in std_logic_vector(data_width-1 downto 0); re: in std_logic; we: in std_logic ); end; architecture rtl of onchip_ram is type memory_t is array(2**addr_width-1 downto 0) of std_logic_vector(data_width-1 downto 0); signal ram: memory_t; signal addr_num: integer; begin addr_num <= decode_unsigned(addr); process(clock) begin if rising_edge(clock) then if std_match(we, '1') then ram(addr_num) <= wq; end if; if std_match(re, '1') then rq <= ram(addr_num); else rq <= (others => 'Z'); end if; end if; end process; end;library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.utility.all; entity data_memory_controller is port ( clock: in std_logic; addr: in std_logic_vector(31 downto 0); rq: out std_logic_vector(31 downto 0); wq: in std_logic_vector(31 downto 0); re: in std_logic; we: in std_logic; ledr: out std_logic_vector(9 downto 0); sw: in std_logic_vector(9 downto 0); hex0: out std_logic_vector(0 to 7); hex1: out std_logic_vector(0 to 7); hex2: out std_logic_vector(0 to 7); hex3: out std_logic_vector(0 to 7); hex4: out std_logic_vector(0 to 7); hex5: out std_logic_vector(0 to 7) ); end; architecture rtl of data_memory_controller is signal c0, c1, c2, c3: std_logic; begin c0 <= '1' when std_match(addr, "000000000000000000000000--------") else '0'; c1 <= '1' when std_match(addr, "10000000000000000000000000000000") else '0'; c2 <= '1' when std_match(addr, "10000000000000000000000000000001") else '0'; c3 <= '1' when std_match(addr, "1000000000000000000000000000001-") else '0'; onchip_ram_c: entity work.onchip_ram generic map( addr_width => 8, data_width => 32 ) port map ( clock => clock, addr => addr(7 downto 0), rq => rq, wq => wq, re => re and c0, we => we and c0 ); ledr_c: entity work.ledr_controller port map( clock => clock, rq => rq, wq => wq, re => re and c1, we => we and c1, ledr => ledr ); sw_c: entity work.sw_controller port map( clock => clock, rq => rq, re => re and c2, sw => sw ); hex_c: entity work.hex_controller port map( clock => clock, addr => addr(0 downto 0), rq => rq, wq => wq, re => re and c3, we => we and c3, hex0 => hex0, hex1 => hex1, hex2 => hex2, hex3 => hex3, hex4 => hex4, hex5 => hex5 ); end;

 

 

This successfully infers on-chip RAM and compile has been successful, with the following warning: "Warning (13046): Tri-state node(s) do not directly drive top-level pin(s)" and 32 instances of "Warning (13048): Converted tri-state node "data_memory_controller:data_memory_controller_c|rq[<31 downto 0>]" into a selector"

 

To elimate this warning, I tried to change to wired-or: if chip is not read enabled, output 0's, and then, or-ing all perpheral outputs.

 

 

 

library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.utility.all; entity onchip_ram is generic ( addr_width: integer; data_width: integer ); port ( clock: in std_logic; addr: in std_logic_vector(addr_width-1 downto 0); rq: out std_logic_vector(data_width-1 downto 0); wq: in std_logic_vector(data_width-1 downto 0); re: in std_logic; we: in std_logic ); end; architecture rtl of onchip_ram is type memory_t is array(2**addr_width-1 downto 0) of std_logic_vector(data_width-1 downto 0); signal ram: memory_t; signal addr_num: integer; begin addr_num <= decode_unsigned(addr); process(clock) begin if rising_edge(clock) then if std_match(we, '1') then ram(addr_num) <= wq; end if; if std_match(re, '1') then rq <= ram(addr_num); else rq <= (others => '0'); -- Here is changed end if; end if; end process; end;library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.utility.all; entity data_memory_controller is port ( clock: in std_logic; addr: in std_logic_vector(31 downto 0); rq: out std_logic_vector(31 downto 0); wq: in std_logic_vector(31 downto 0); re: in std_logic; we: in std_logic; ledr: out std_logic_vector(9 downto 0); sw: in std_logic_vector(9 downto 0); hex0: out std_logic_vector(0 to 7); hex1: out std_logic_vector(0 to 7); hex2: out std_logic_vector(0 to 7); hex3: out std_logic_vector(0 to 7); hex4: out std_logic_vector(0 to 7); hex5: out std_logic_vector(0 to 7) ); end; architecture rtl of data_memory_controller is signal c0, c1, c2, c3: std_logic; signal q0, q1, q2, q3: std_logic_vector(31 downto 0); -- Here is added begin c0 <= '1' when std_match(addr, "000000000000000000000000--------") else '0'; c1 <= '1' when std_match(addr, "10000000000000000000000000000000") else '0'; c2 <= '1' when std_match(addr, "10000000000000000000000000000001") else '0'; c3 <= '1' when std_match(addr, "1000000000000000000000000000001-") else '0'; onchip_ram_c: entity work.onchip_ram generic map( addr_width => 8, data_width => 32 ) port map ( clock => clock, addr => addr(7 downto 0), rq => q0, -- Here is changed wq => wq, re => re and c0, we => we and c0 ); ledr_c: entity work.ledr_controller port map( clock => clock, rq => q1, -- Here is changed wq => wq, re => re and c1, we => we and c1, ledr => ledr ); sw_c: entity work.sw_controller port map( clock => clock, rq => q2, -- Here is changed re => re and c2, sw => sw ); hex_c: entity work.hex_controller port map( clock => clock, addr => addr(0 downto 0), rq => q3, -- Here is changed wq => wq, re => re and c3, we => we and c3, hex0 => hex0, hex1 => hex1, hex2 => hex2, hex3 => hex3, hex4 => hex4, hex5 => hex5 ); rq <= q0 or q1 or q2 or q3; -- Here is added end;

 

However, this failed inferring on-chip RAM with following message: "Info (276007): RAM logic "data_memory_controller:data_memory_controller_c|onchip_ram:onchip_ram_c|ram" is uninferred due to asynchronous read logic"

 

 

I'm confusing why using 'Z' for read-enable control infers RAM while using '0' uninfers.

 

 

I'm using Quartus Prime Lite 18.0.0, and target device is MAX 10 family.

0 Kudos
1 Reply
Tricky
New Contributor II
146 Views

First of all, FPGAs do not have internal tri-state drivers, so it is only valid to drive an IO pin with a 'Z' state, so they are removed, as the warning indicate.

 

Second, your "wired-or" approach: it uses the read-enable as the select line on a mux to select either the memory read data or 0. The problem is, the internal rams do not have such a mux built in them, so it has to create the rams out of LUTs to match the functionality that you wrote.

I wouldnt do a wired or - you will need a arbitrator to mux between the blocks. (probably just a priority encoder, as each memory should be accessed in a mutually exclusive way?)

 

The synthesis handbook has the templates you need to infer rams.

 

Also not: why the use of std_match everywhere? why not just re = '1'? FPGAs only have '1' or '0' state. It can be useful to be explicit, rather than allow through '-' state in simulation.

Reply