我的代码模拟存在问题。我有一个由双端口内存组成的异步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的允许值为:
其他值会导致模拟中的错误(索引约束违规)。问题是由于延迟,我的数字可能大于1001。例如,如果addr_rd_i是0111并且我想读取1000,则可能在短时间内我有1111:
现在的问题是:有没有办法避免模拟错误?我想到这样的事情:
MEM_READ:data_o <= memory(to_integer(unsigned(addr_rd_i)) mod MEM_DEPTH);
我唯一的(大)担心是我可能无法为合成保留相同版本的文件,所以我需要2个文件,一个用于综合,一个用于模拟。
答案 0 :(得分:2)
为synth和sim保留相同的文件!!!
使用和不使用&#34; mod MEM_DEPTH&#34;进行合成。如果它们的大小相同,那么综合优化已经删除了MOD运算符......那么,没问题。
我的首选方法:写一个&#34; to_address&#34;函数执行所有类型转换,返回有效地址。在--pragma translate off
和--pragma translate on
之间包含一个涉及MOD运算符的return语句(请参考您的synth工具获取实际接受的语法)。按照简单的退货声明......
请注意,读写地址应该首先声明为unsigned。只要您进行级联类型转换,设计可能出现问题......
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
,而合成将落到第二个。PackageManager
。注释并坚持手动检查此功能来代码审查时间......
答案 1 :(得分:0)
撇开为什么有人想要将时间抖动刺激应用于零时间模型的问题,除了使用函数调用来过滤&#39;之外还有另一个解决方案。地址。
异步RAM使用的经典模型包括读取使能,用于通过在读取使能为真时要求地址稳定来降低功耗。 EMI降低与节能同时进行。这同样可以应用于真正的异步写端口,要求使用只在地址稳定时才会发生的写使能信号通知非时钟写。时钟读取和写入很容易,假设地址在时钟边缘是稳定的。
VHDL中的默认模型是惯性延迟,其模拟切换延迟 - 与OP相关,其中所有地址&#39;&#39;不能同时传播,当内存大小不是2的幂时,导致零时间内存模型的超出范围索引范围。
惯性延迟也有一个拒绝时间,用于消除短于拒绝时间的脉冲。要求拒绝时间小于相关延迟,并且拒绝时间具有在信号分配的第一个波形元素中指定的延迟时间的默认值(可以是唯一的元素)
(参见IEEE Std 1076-2008 10.5.2简单信号分配。)
信号更新在VHDL中以预计输出波形队列进行调度,该队列包含值更新的值和时间。
我们不能简单地使用拒绝时间安排data_o更新,因为排队的值需要读取当前内存内容。
我们可以使用依赖于它的拒绝限制的惯性延迟模型来过滤掉read_addr_i输入上的开关噪声。
因为您需要索引内存读取的整数(或自然)索引,您可以添加另一个信号,保持读取地址的整数值并执行脉冲拒绝(并且分配的延迟等于或大于拒绝时间表达)。
除此之外,IEEE Std 1076.6-2004(RTL合成,现已撤回)是支持的合成结构生成硬件的基础。综合供应商仍将此作为起点。在8.8.4信号赋值语句中,我们可以看到忽略了延迟机制。后面和后面的时间表达式也被忽略(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持续时间低于拒绝限制。
注意,异步复位用于将内存的内容归零。