ISE 自动推断 RAM 块需要满足哪些要求?

发布于 2024-10-17 09:08:42 字数 4030 浏览 0 评论 0 原文

我有一段 IP,它应该是 32 位字节可寻址内存。但我无法让它推断块内存,它正在推断大量的触发器...

它应该适合仅具有双端口块内存的 Spartan3e (xc3s1200e-4fg320),实际上内存被分为两个数组按奇偶排列...

这是代码,我希望可以帮助理解我做错了什么?

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);
             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;

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);
             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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

寂寞清仓 2024-10-24 09:08:43

Xilinx 综合指南 下的 FPGA 流程编码。我几乎可以肯定重置循环会导致推断失败。该代码需要同时访问存储器的所有元素,而这对于 Block RAM 来说是不可能的。

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.

煮茶煮酒煮时光 2024-10-24 09:08:43

你不能这样做:

process (clk, rst)
begin
    if rst = '1' then
        for a in 0 to 2047 loop
            memory0(a) <= INIT(a*2);
        end loop;

...因为这需要一个可重置内存,而不是一个初始化内存。

要初始化,您需要将信号声明更改为以下形式

signal memory0 : memory_t(0 to 2047) := ( some list of integers or something that returns an array of integers);

您当前执行此操作的方式(与您的 init 交错)意味着您必须使用一个函数:(

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;

请注意,这是我凭空想象出来的我什至没有尝试编译它,所以对任何拼写错误和语法错误表示歉意......但我希望你明白:)

然后你可以用它来初始化信号:

signal memory0 : memory_t(0 to 2047) := init_mem(INIT);

这将全部用于模拟。您可能会或可能不会成功使用 XST 合成器推断 INIT 值 - 我还没有尝试过。检查综合日志文件以查看其报告的内容 - 请向我们报告它是否有效以及您在哪个版本的 XST 上尝试过它。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文