What are the requirements to meet in order to ISE auto infer ram blocks?
I have this piece of IP that is supposed to be a 32 bits byte addressable memory. But I can't make it infer block rams, it is inferring a huge amount of flip flops...
It is supposed to fit on a Spartan3e (xc3s1200e-4fg320) which has only dual port block rams, indeed the memory is split into two arrays in an even-odd arrange...
Here is the code, I hope that might help understand what am I doing wrong?
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
package mem_types is
type memory_t is array (natural range <>) of std_logic_vector(7 downto 0);
end mem_types;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.mem_types.all;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity ram is
generic (
INIT : memory_t(0 to 4095) := (others => (others => '0'))
);
port ( clk, rst : in std_logic;
addr : in std_logic_vector(11 downto 0);
din : in std_logic_vector(31 downto 0);
dout : out std_logic_vector(31 downto 0);
开发者_JAVA技巧we : std_logic_vector(3 downto 0)
);
end ram;
architecture Behavioral of ram is
type ramport_t is record
addr : std_logic_vector(10 downto 0);
dout : std_logic_vector(7 downto 0);
din : std_logic_vector(7 downto 0);
wea : std_logic;
end record;
signal port0a, port0b, port1a, port1b : ramport_t;
signal addr_a, addr_b, addr_c, addr_d : std_logic_vector(11 downto 0);
signal memory0, memory1 : memory_t(0 to 2047);
begin
addr_a <= addr;
addr_b <= addr+1;
addr_c <= addr+2;
addr_d <= addr+3;
port0a.addr <= addr_a(11 downto 1) when addr_a(0) = '0' else addr_b(11 downto 1);
port1a.addr <= addr_b(11 downto 1) when addr_b(0) = '1' else addr_a(11 downto 1);
port0b.addr <= addr_c(11 downto 1) when addr_c(0) = '0' else addr_d(11 downto 1);
port1b.addr <= addr_d(11 downto 1) when addr_d(0) = '1' else addr_c(11 downto 1);
dout(07 downto 00) <= port0a.dout when addr_a(0) = '0' else port1a.dout;
dout(15 downto 08) <= port1a.dout when addr_b(0) = '1' else port0a.dout;
dout(23 downto 16) <= port0b.dout when addr_c(0) = '0' else port1b.dout;
dout(31 downto 24) <= port1b.dout when addr_d(0) = '1' else port0b.dout;
port0a.din <= din(07 downto 00) when addr_a(0) = '0' else din(15 downto 08);
port1a.din <= din(15 downto 08) when addr_b(0) = '1' else din(07 downto 00);
port0b.din <= din(23 downto 16) when addr_c(0) = '0' else din(31 downto 24);
port1b.din <= din(31 downto 24) when addr_d(0) = '1' else din(23 downto 16);
port0a.wea <= we(0) when addr_a(0) = '0' else we(1);
port1a.wea <= we(1) when addr_b(0) = '1' else we(0);
port0b.wea <= we(2) when addr_c(0) = '0' else we(3);
port1b.wea <= we(3) when addr_d(0) = '1' else we(2);
port0a.dout <= memory0(conv_integer(port0a.addr));
port0b.dout <= memory0(conv_integer(port0b.addr));
port1a.dout <= memory1(conv_integer(port1a.addr));
port1b.dout <= memory1(conv_integer(port1b.addr));
process (clk, rst)
begin
if rst = '1' then
for a in 0 to 2047 loop
memory0(a) <= INIT(a*2);
end loop;
elsif falling_edge(clk) then
if (port0a.wea = '1') then
memory0(conv_integer(port0a.addr)) <= port0a.din;
end if;
if (port0b.wea = '1') then
memory0(conv_integer(port0b.addr)) <= port0b.din;
end if;
end if;
end process;
process (clk, rst)
begin
if rst = '1' then
for a in 0 to 2047 loop
memory1(a) <= INIT((a*2)+1);
end loop;
elsif falling_edge(clk) then
if (port1a.wea = '1') then
memory1(conv_integer(port1a.addr)) <= port1a.din;
end if;
if (port1b.wea = '1') then
memory1(conv_integer(port1b.addr)) <= port1b.din;
end if;
end if;
end process;
end Behavioral;
This is described in the Xilinx Synthesis Guide under Coding for FPGA Flow. I'm almost certain that reset loop is causing flops to be inferred. That code requires accessing all elements of the memory simultaneously, which is not possible with Block RAM.
You can't do this:
process (clk, rst)
begin
if rst = '1' then
for a in 0 to 2047 loop
memory0(a) <= INIT(a*2);
end loop;
...as that is asking for a resettable memory, rather than an initialised one.
To initialise, you need to change your signal declaration to be of the form
signal memory0 : memory_t(0 to 2047) := ( some list of integers or something that returns an array of integers);
The way you are currently doing it (with your init's interleaved) means you'll have to use a function:
function init_mem(init_values: memory_t) returns memory_t is
variable retval : memory_t(init_values'high/2)+1 downto 0);
begin
for i in retval'range loop
retval(i) := init_values(2*i);
end for;
end function;
(note that that was typed off the top of my head and I've not even tried to compile it, so apologies for any typos and syntax errors... but I hope you get the idea :)
Then you can use this to init the signal:
signal memory0 : memory_t(0 to 2047) := init_mem(INIT);
This will all work for simulation. You may or may not have success with the XST synthesiser inferring the INIT values - I haven't tried. Check the synthesis logfile to see what it reports - and please do report back to us whether it works and on what version of XST you tried it.
精彩评论