vhdl 中的索引约束冲突

Index constraint violation in vhdl

我的代码模拟有问题。我有一个由双端口存储器组成的异步 FIFO。写入与写入时钟同步执行,读取执行提供我要读取的位置的地址。同步由读写指针执行。

基本上我有这样的东西:

architecture rtl of async_memory is 
     type RAM is array (MEM_DEPTH - 1 downto 0) of std_logic_vector(DATAWIDTH - 1 downto 0);
     signal memory : RAM;

begin
     MEM_WRITE:process(clk,rstn)
     begin
     .....
        memory(to_integer(unsigned(addr_wr_i))) <= data_i;
     .....
     end process;

     MEM_READ:data_o <= memory(to_integer(unsigned(addr_rd_i)));

当 MEM_DEPTH 是 2 的幂时,我没有遇到任何问题。当 MEM_DEPTH 不是 2 的幂时,我在为 addr_rd_i (读取的地址信号)的每条线模拟随机延迟时遇到一些问题。

明确一点,如果我设置的MEM_DEPTH为10,那么addr_rd_i的宽度就是4位。 addr_rd_i 的允许值为:

  1. 0000
  2. 0001
  3. 0010
  4. 0011
  5. 0100
  6. 0101
  7. 0110
  8. 0111
  9. 1000
  10. 1001

其他值当然会导致模拟错误(违反索引约束)。问题是由于延迟,我可以拥有大于 1001 的数字。例如,如果 addr_rd_i 是 0111 并且我想读取 1000,则可能在短时间内我有 1111 :

现在的问题是:有没有办法避免模拟错误?我想是这样的:

MEM_READ:data_o <= memory(to_integer(unsigned(addr_rd_i)) mod MEM_DEPTH);

我唯一(大)担心的是我可能无法为合成保留相同版本的文件,所以我需要 2 个文件,一个用于合成,一个用于模拟。

为 synth 和 sim 保留相同的文件!!!

使用和不使用 "mod MEM_DEPTH" 进行合成。如果它们的大小相同,则综合优化已删除 MOD 运算符......然后,没问题。

我的首选方法:编写一个 "to_address" 函数执行所有类型转换,returning 一个有效地址。在 --pragma translate off--pragma translate on 之间包装一个涉及 MOD 运算符的 return 语句(有关实际接受的语法,请咨询您的合成工具)。用一个简单的 return 语句跟随它...

请注意,读写地址应该首先声明为无符号的。任何时候级联类型转换,设计都可能有问题...

function to_address(addr : unsigned) return natural is
   temp : natural := to_integer(addr);
begin
   --pragma translate off
   return temp mod MEM_DEPTH;
   --pragma translate on
   return temp;
end to_address.

然后模拟将达到第一个 return 而合成将落到第二个。将其注释掉并坚持在代码审查时间来手动检查此功能...

抛开为什么有人想要将时间抖动刺激应用于零时间模型的问题,除了使用函数调用 'filter' 地址之外还有另一种解决方案。

异步 RAM 使用的经典模型将包括一个读取启用,它用于通过要求地址在读取启用为真期间保持稳定来降低功耗。降低 EMI 与节能也息息相关。这同样适用于真正的异步写入端口,该端口需要非时钟写入以仅在地址稳定时发生的写入使能信号通知。时钟读写很容易,假设地址在时钟边沿稳定。

VHDL 中的默认模型是惯性延迟,它模拟切换延迟 - 与 OP 相关,其中所有地址 'bits' 不会同时传播,导致超出索引范围当内存大小不是 2 的幂时的零时间内存模型。

惯性延迟也有拒绝时间,用来剔除短于拒绝时间的脉冲。要求拒绝时间小于相关延迟,并且拒绝时间默认为信号分配的第一个波形元素(可以是唯一元素)中指定的延迟时间

(参见 IEEE Std 1076-2008 10.5.2 简单信号分配。)

信号更新在 VHDL 中安排在投影输出波形队列中,其中包含发生值更新的值和时间。

我们不能简单地安排一个带有拒绝时间的 data_o 更新,因为排队的值需要读取当前内存内容。

我们可以使用惯性延迟模型滤除 read_addr_i 输入上的开关噪声,依靠它的拒绝限制。

因为索引内存读取需要一个整数(或自然)索引,所以您可以添加另一个信号,保存读取地址的整数值并执行脉冲抑制(以及分配延迟等于或大于拒绝时间表达)。

顺便说一句,IEEE Std 1076.6-2004(RTL 综合,现已撤回)是生成硬件的受支持综合构造的基础。综合供应商仍将以此为起点。在 8.8.4 信号赋值语句中我们可以看到延迟机制被忽略了。 after 和 after 之后的时间表达式也将被忽略 (8.8.4.1)。

因此我们可以将时序添加到我们的零时间模型以支持仿真中的脉冲抑制:

architecture fum of async_memory is
    type RAM is array (MEMDEPTH - 1 downto 0) of 
            std_logic_vector(DATAWIDTH - 1 downto 0);
    signal memory:  RAM;
    signal addr_rd: natural;
begin

MEM_WRITE:
    process( clk, rstn)
    begin
        if rstn = '0' then
            memory <= (others => (others => '0'));
        elsif rising_edge(clk) then
            if wen = '0' then
                memory(to_integer(unsigned(addr_wr_i))) <= data_i;
            end if;
        end if;
    end process;
MEM_READ:
    data_o <= memory(addr_rd);
PULSE_REJECT:
    addr_rd <= reject 1.8 ns inertial to_integer(unsigned(addr_rd_i)) after 1.9 ns;
end architecture;

拒绝限制是根据模型时钟周期(此处为 10 ns)选择的,我们看到地址需要 1.9 ns 才能生效,表示读取延迟(请记住分配延迟必须等于或大于拒绝限制)。拒绝限制表示任意两条地址线之间切换时间的差异。

在测试台中,我们可以对 OP 的示例进行建模:

STIMULI:
    process
    begin
        wait for 6 ns;
        rstn <= '0';
        wait for 10 ns;
        rstn <= '1';
        wait for 20 ns;
        data_i <= x"ac";
        addr_wr_i <= x"8";
        wen <= '0';
        wait for 10 ns;
        wen <= '1';
        addr_rd_i <= "0111";
        wait for 10 ns;
        addr_rd_i(addr_rd_i'LEFT)  <= '1';
        wait for 1.7 ns;
        addr_rd_i(addr_rd_i'LEFT -1 downto 0) <= (others => '0');
        wait for 8.3 ns;
        wait;
    end process; 

地址从 0111 翻转到 1000,MSB 比 LSB 快。

这给了我们:

其中地址值 F duration 低于拒绝限制。

请注意异步重置用于将内存内容归零。