- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page