Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
16595 Discussions

Inferring RAM with read enable

BLee15
New Contributor I
1,162 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
399 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.

0 Kudos
Reply