我是VHDL的初学者。我想知道为什么在下面的代码中有一个循环的延迟。以及如何避免它..同时在verilog声明总是@(posedge clk)没有任何延迟..如何做同样的事情VHDL
library IEEE;
use IEEE.std_logic_1164.all;
-- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
end t_ff_s;
-- entity
architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
begin
tff: process (S,rising_edge(clk))
begin
if (S = '0') then
t_tmp <= '1';
--elsif (rising_edge(CLK)) then
else
t_tmp <= T XOR t_tmp; -- temp output assignment
end if;
end process tff;
Q <= t_tmp; -- final output assignment
end my_t_ff_s;
答案 0 :(得分:3)
VHDL中的灵敏度列表不像Verilog那样采用边缘规范。 VHDL更灵活,您可以在流程中的任何位置自由使用'event
信号属性来实现边缘触发行为。您可以混合使用级别和边缘敏感逻辑,而无需使用拆分块/进程或诸如negedge
之类的黑客来进行重置。在敏感性列表中不允许使用rising_edge(clk)
(实现clk'event
的测试)等函数调用。它只包含信号名称。您的代码不会按原样编译。
如果您的代码的某些其他语法正确版本编译干净,您看到的延迟是模拟模型的工件或具有损坏的敏感性列表。如果您想要一个同步时钟驱动的过程,那么您只需要时钟信号,并可能需要在灵敏度列表中进行异步复位。
考虑以下过程:
tff: process(S, clk)
begin
if S = '0' then -- Asynchronous reset (level sensitive)
t_tmp <= '1';
elsif rising_edge(clk) then -- Synchronous logic (edge sensitive)
t_tmp <= T xor t_tmp;
end if;
end process;
Q <= t_tmp;
当S
或clk
上发生事件时,将执行此流程。如果S为'0',则执行重置条件优先于elsif
子句(clk
是无关紧要的)。对t_tmp
的赋值对下一个delta周期生效,该周期仍与当前模拟时间相同。否则,如果rising_edge(clk)
评估为真,则clk
上发生一个事件,并且状态从'0'(或'L')变为'1'(或'H'),表明事件是上升的边缘。同步分配发生,新的xored t_tmp
在下一个增量循环中生效。 T
中的更改不会导致进程执行,因为它不是(也不应该)在敏感列表中。
因为没有无条件else
子句,如果两个t_tmp
条件都为假,则if
信号会保留其最后指定的值。它会在下次S
或clk
上发生导致t_tmp
新作业的事件时发生变化。这将是下一个时钟沿或重新应用异步复位。
Q
的分配是连续的,与其敏感度列表中t_tmp
的过程实际上相同。因此,对Q
的赋值在t_tmp
上的事件之后发生增量循环,这是在上升沿之后的两个增量循环。如果Q
正在进入比边缘的第二个增量周期更早更新的逻辑,则它将需要额外的时钟周期才能传播。
在检查波形时,围绕增量周期的行为有时会产生令人困惑的结果。您可能有一个上升沿应捕获出现的数据输入,以便在同一时间步骤同时转换,实际上,数据在稍后的增量循环中转换,并且仅在下一个时钟边缘。
类似地,如果您构造一个没有任何时间延迟的简单门控时钟,它的边缘将同时发生,但是在后来的delta周期发生,而不是时钟的非门控版本。从“早期”非门控时钟驱动的数据将由门控逻辑捕获比结果预期更早的时钟周期。从另一个方向驱动的数据似乎会在一个时钟周期内出现意外延迟。
目前尚不清楚是什么原因造成了您所看到的问题,而没有关于您如何驾驶S
,T
和clk
信号的更多信息,但它可能已连接到模拟引擎的delta循环行为在某种程度上。
答案 1 :(得分:2)
问题
比凯文更简洁一点,rising_edge是一个表达而不是信号,灵敏度列表需要一个命名信号,一个事务,你可以继续执行暂停的进程。把elsif放回去,灵敏度列表中只有S和clk。
请注意,由于t_tmp不在敏感度列表中,因此您不会看到Q跟随t_tmp,直到导致您注意到延迟的下一个时钟事件。
固定语法过程:
tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
begin
if (S = '0') then
t_tmp <= '1';
elsif (rising_edge(CLK)) then -- put back
-- else
t_tmp <= T XOR t_tmp; -- temp output assignment
end if;
Q <= t_tmp; -- final output assignment
end process tff;
显示t_tmp和Q之间的延迟:
通过使Q成为并发信号分配来修复它
为了解决半个时钟延迟问题,您可以为Q分配并发信号赋值语句(将其移到进程外)。
tff:
process (S, clk)
begin
if S = '0' then
t_tmp <= '1';
elsif rising_edge(clk) then
t_tmp <= T xor t_tmp;
end if;
end process;
Q <= t_tmp; -- concurrent signal assignment
给出了:
您可以在上面看到t_tmp和Q现在处于同步状态。
通过将t_tmp变为
来解决此问题您还可以将t_tmp声明为进程dff中的变量而不是信号,并将切换分配给它,因为变量赋值也将固化t_tmp和Q之间的一个时钟延迟。
tff:
process (S, clk)
variable t_tmp: std_logic;
begin
if S = '0' then
t_tmp := '1';
elsif rising_edge(clk) then
t_tmp := T xor t_tmp;
end if;
Q <= t_tmp;
end process;
显示:
使用gtkwave的ghdl不输出变量或显示delta周期。你可以看到Q出现在时钟的上升沿。
使t_tmp变量也可以消除t_tmp上的事务和Q上的事务之间的增量循环。
消除delta周期使您的模型执行得更快(在当前模拟时发生)。在任何进程执行且变量赋值立即生效时,信号分配不会生效。
通过将t_tmp添加到敏感度列表
来修复它另外,您可以将t_tmp添加到灵敏度列表中(以及S和clk)。
tff:
process (S, clk, t_tmp)
begin
if S = '0' then
t_tmp <= '1';
elsif rising_edge(clk) then
t_tmp <= T xor t_tmp;
end if;
Q <= t_tmp;
end process;
这比所有其他修复都要慢,因为每次t_tmp有一个事件以及S或CLK时都会执行if语句。 rising_edge是一个函数调用,它动态地详细说明了它的接口列表,这是一个重要的模拟器性能损失,特别是如果你使用了很多这些原语。
这些是通过测试台完成的:
library IEEE;
use IEEE.std_logic_1164.all;
-- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
end entity t_ff_s;
architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
begin
tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
begin
if (S = '0') then
t_tmp <= '1';
elsif (rising_edge(CLK)) then -- put back
-- else
t_tmp <= T XOR t_tmp; -- temp output assignment
end if;
Q <= t_tmp; -- final output assignment
end process tff;
end my_t_ff_s;
architecture foe of t_ff_s is
signal t_tmp: std_logic;
begin
tff:
process (S, clk)
begin
if S = '0' then
t_tmp <= '1';
elsif rising_edge(clk) then
t_tmp <= T xor t_tmp;
end if;
end process;
Q <= t_tmp; -- concurrent signal assignment
end architecture;
architecture fie of t_ff_s is
begin
tff:
process (S, clk)
variable t_tmp: std_logic;
begin
if S = '0' then
t_tmp := '1';
elsif rising_edge(clk) then
t_tmp := T xor t_tmp;
end if;
Q <= t_tmp;
end process;
end architecture;
architecture fee of t_ff_s is
signal t_tmp: std_logic;
begin
tff:
process (S, clk, t_tmp)
begin
if S = '0' then
t_tmp <= '1';
elsif rising_edge(clk) then
t_tmp <= T xor t_tmp;
end if;
Q <= t_tmp;
end process;
end architecture;
library ieee;
use ieee.std_logic_1164.all;
entity test_tff is
end entity;
architecture foo of test_tff is
signal CLK: std_logic := '0';
signal T: std_logic := '0';
signal S: std_logic := '0';
signal Q: std_logic;
component t_ff_s is
port (
signal CLK: in std_logic;
signal T: in std_logic;
signal S: in std_logic;
signal Q: out std_logic
);
end component;
begin
DUT:
t_ff_s
port map (
T => T,
S => S,
CLK => CLK,
Q => Q
);
CLOCK:
process
begin
wait for 10 ns;
CLK <= not CLK;
if Now > 250 ns then
wait;
end if;
end process;
SET:
process
begin
S <= '0';
wait for 20 ns;
S <= '1';
wait;
end process;
TOGGLE:
process
begin
wait for 20 ns;
T <= '1';
wait for 60 ns;
T <= '0';
wait for 40 ns;
T <= '1';
wait;
end process;
end architecture;
configuration my_t_ff_s_config of test_tff is
for foo
for DUT: t_ff_s
use entity work.t_ff_s(my_t_ff_s);
end for;
end for;
end configuration;
configuration concurrent_config of test_tff is
for foo
for DUT: t_ff_s
use entity work.t_ff_s(foe);
end for;
end for;
end configuration;
configuration variable_config of test_tff is
for foo
for DUT: t_ff_s
use entity work.t_ff_s(fie);
end for;
end for;
end configuration;
configuration sensitivity_config of test_tff is
for foo
for DUT: t_ff_s
use entity work.t_ff_s(fee);
end for;
end for;
end configuration;
请注意使用配置
使用VHDL的配置声明允许使用多种体系结构。 (my_t_ff_s - 原始的,敌人 - 同时分配给Q,fie - 将t_tmp作为变量和费用 - 在灵敏度列表中使用t_tmp)。
令人惊讶的是,ghdl的分析器对于正确获取配置声明语法非常有帮助。一旦你拿到第一个,其他的很容易。
我们倾向于使用配置变得生疏,历史上通常不会通过综合工具支持它。但话说回来,这是模拟验证。
对于那些有ghdl和gtkwave的人来说,这就是它的完成方式:
ghdl -a t_ff.vhdl
ghdl -e my_t_ff_s_config
ghdl -e concurrent_config
ghdl -e concurrent_config
ghdl -e sensitivity_config
ghdl -r my_t_ff_s_config --wave = test_tff_my_t_ff_s.ghw
ghdl -r concurrent_config --wave = test_tff_foe.ghw
ghdl -r variable_config --wave = test_tff_fie.ghw
ghdl -r sensitivity_config --wave = test_tff_fee.ghwGHW是ghdl的原生波形转储文件格式,由gtkwave理解。
在gtkwave中:
打开t_ff_s.gtkw(读入test_tff_my_t_ff_s.ghw)
(否则请阅读test_tff_my_t_ff_s.ghw并将信号添加到
波形显示,格式化窗口,将保存文件保存到t_ff_s.gtkw)新标签打开test_tff_foe.ghw
读取保存文件打开t_ff_s.gtkw
新标签打开test_tff_fie.ghw
读取保存文件打开t_ff_s.gtkw
新标签打开test_tff_fee.ghw
读取保存文件打开t_ff_s.gtkw注意ghdl不保存变量状态或delta周期,t_tmp不会出现在test_ff_fie.ghw的波形中。