我有以下代码的简化示例,其中可以模拟DeltasTest
实体以显示问题。实际设计中的时钟是基于通用的反转或不反转,并且在此之下提供其他几个实体。
问题在于简单的边缘检测器在行为模拟中不起作用(data_out
只是一个小故障),这是由于反转阶段在时钟上引入的delta周期延迟。是否有标准或其他优雅方式来解决这个问题?
到目前为止,我最好的解决方案是将data_in
信号分配给另一个信号,使其具有与clk
相同的增量周期延迟。我想根据通用作为函数的第二个参数使用函数来根据需要反转时钟,但是时钟在很多地方使用,这看起来并不优雅,我注意到它甚至可以解决问题。抓住稻草,我也尝试将时钟倒置分配为transport
任务,但正如预期的那样,这没有任何区别。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity Deltas is
Generic (
CLK_INVERT : boolean := false
);
Port (
clk : in std_logic;
data_in : in std_logic
);
end Deltas;
architecture Behavioral of Deltas is
-- Signals
signal data_reg : std_logic := '0';
signal clk_inverted : std_logic := '0';
signal data_out : std_logic := '0';
begin
ClkInvert : if (CLK_INVERT) generate
clk_inverted <= not clk;
else generate
clk_inverted <= clk;
end generate;
process (clk_inverted)
begin
if (rising_edge(clk_inverted)) then
data_reg <= data_in;
end if;
end process;
process (data_reg, data_in)
begin
if (data_reg /= data_in) then
data_out <= '1';
else
data_out <= '0';
end if;
end process;
-- Other entities use `clk_inverted`. Commented out so that this example compiles
--LowerEntity : entity work.Counter
--port map (
-- clk => clk_inverted
--);
end Behavioral;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity DeltasTest is
end DeltasTest;
architecture Behavioral of DeltasTest is
signal clk : std_logic := '0';
signal data_in : std_logic := '0';
begin
clk <= not clk after 10 ns;
process (clk)
variable count : integer := 0;
begin
if (rising_edge(clk)) then
count := count + 1;
if (count = 4) then
count := 0;
data_in <= not data_in;
end if;
end if;
end process;
uut : entity work.Deltas
Port map (
clk => clk,
data_in => data_in
);
end Behavioral;
答案 0 :(得分:3)
我认为data_in
上额外增量周期的“解决方案”可能是最简洁的解决方案。
从语义上讲,对clk_inverted
的分配可能会转换为硬件作为时钟信号中的反相器,因此仿真中的增量循环延迟表示在实际硬件中引入的(可能(*)实际)竞争条件。 / p>
因此,模拟行为不仅仅是一种烦恼,而且是一种有用的属性,警告您可能会偏离必须遵守的良好同步设计实践。
(*)只有“可能真实”,因为根据技术的不同,合成可以通过检测相反的边缘而将反转“推”到下游FF - 根据Morten的评论自动转换你的设计。
当额外的delta周期隐藏在别人的IP中时,这可能是一个问题,可能是外部组件(如内存)的模型。然后,IP不再受您的控制,并且板级仿真可能会在错误的时钟周期内读取或写入数据。
在这里,我的答案是增加近似的I / O延迟,或者作为FPGA I / O分配的惯性延迟(来自FPGA数据表或综合报告),或者作为PCB上的传输延迟。这确保了我的模拟接近电路板的行为 - 不完美,但足够接近,以便我能够在正常的同步练习之后设计内部逻辑。
更清洁的解决方案是基于通用生成两个不同的时钟进程。这完全消除了讨厌的delta周期。
最后!用于2过程形式的SM,其中保持时钟过程尽可能简单有益...
没那么快......
您要重构的行为是时钟边缘检测,它已经从过时的和复杂的(级别和事件)表达式重构为一个简单的函数。
为什么不进一步重构时钟边沿检测,例如......
function clock_edge(signal clk : in std_logic) return boolean is
begin
if CLK_INVERT then
return falling_edge(clk);
else
return rising_edge(clk);
end if;
end clock_edge;
...
process (my_clock) is
begin
if clock_edge(my_clk) then ...
我没有对此进行测试,以确定综合工具是否可以根据需要实现此功能。有些可能会,有些可能不会(如果他们只是特别提供系统提供的功能而不是正确地完成工作)
在分层设计中,此函数将位于包中,由需要它的任何实体使用。这会产生通用可见性的问题:一个建议是在包中使用常量,另一个建议是将泛型作为第二个参数传递给函数。正确的方法可能取决于综合工具施加的限制。
答案 1 :(得分:1)
如何使用旧式时钟并将CLK_INVERT替换为CLK_POLARITY。我自己没有这样做,但我很确定我已经在某个地方看到了它。
entity Deltas is
Generic (
CLK_POLARITY : std_logic := '1'
);
Port (
clk : in std_logic;
data_in : in std_logic
);
end Deltas;
architecture Behavioral of Deltas is
signal data_reg : std_logic;
signal data_out : std_logic;
begin
process (clk)
begin
if clk = CLK_POLARITY and clk'event then
data_reg <= data_in;
end if;
end process;
这引发了一个更大的问题,如何为不同的设计编写IP需要不同的时钟极性(如此处所做),不同的复位极性和不同的复位方式(异步复位与同步复位)。我认为我们需要一套实现触发器的程序。除了具有异步复位的专用内置DFF之外,我认为我们还可以使用DFFR(带复位的DFF),其实现由所使用的封装体决定。
-- In package body asynchronous resets
procedure DFFR (
signal Clk : std_logic ;
signal Reset : std_logic ;
signal DataIn : std_logic ;
signal DataOut : std_logic ;
constant ResetValue : std_logic
) is
begin
if Reset = RESET_POLARITY then
DataOut <= ResetValue ;
elsif Clk = CLOCK_POLARITY and Clk'event then
DataOut <= DataIn ;
end if ;
end procedure DFFR ;
-- In package body synchronous resets
procedure DFFR (
signal Clk : std_logic ;
signal Reset : std_logic ;
signal DataIn : std_logic ;
signal DataOut : std_logic ;
constant ResetValue : std_logic
) is
begin
if Clk = CLOCK_POLARITY and Clk'event then
if Reset = RESET_POLARITY then
DataOut <= ResetValue ;
else
DataOut <= DataIn ;
end if ;
end if ;
end procedure DFFR ;
-- In package body power on reset by initialization
procedure DFFR (
signal Clk : std_logic ;
signal Reset : std_logic ;
signal DataIn : std_logic ;
signal DataOut : std_logic ;
constant ResetValue : std_logic
) is
begin
if Clk = CLOCK_POLARITY and Clk'event then
DataOut <= DataIn ;
end if ;
end procedure DFFR ;
对不同时钟域做出不同决策的设计需要为每个时钟域分别创建一个库。
即将推出的IEEE 1076-2018正在为IEEE开源做一个开源的当前VHDL包。也许我们可以在下一次修订中得到这个 - 但是,目前我有更多的想法,而不是我有时间,所以我们需要更多的参与者。
答案 2 :(得分:0)
我认为您有亚稳态发生的情况。您输入数据,如果它来自外部世界可能不会同步。
通过两个级联寄存器时钟data_in,并在两个寄存器之间进行比较,而不是将data_reg与输入信号进行比较。
我很确定我在
之前犯了这个错误