我有以下代码(原始的“RS-232信令”发射器)......
LIBRARY ieee;
USE ieee.std_logic_1164.all;
entity SerialTX is
port(
baud_clk : in std_logic;
data : in std_logic_vector(7 downto 0);
send : in std_logic;
serial_out : out std_logic := '0';
busy : out std_logic := '0'
);
end entity;
----------------------------------------
architecture behavioural of SerialTX is
constant IDLE_BITS : std_logic_vector(10 downto 0) := "00000000001";
signal shifter : std_logic_vector(10 downto 0) := IDLE_BITS;
signal shift : std_logic := '0';
signal internal_busy : std_logic := '0';
begin
-------- ALWAYS HAPPENING --------
serial_out <= shifter(0);
busy <= internal_busy;
internal_busy <= '1' when (shifter /= IDLE_BITS) else '0';
----------------------------------
shifting_handler:
process(baud_clk) is
begin
if rising_edge(baud_clk) then
if (send = '1') and (shifter = IDLE_BITS) then
shifter <= "11" & data & '0';
elsif (shifter /= IDLE_BITS) then
shifter <= '0' & shifter(10 downto 1); -- shifter >>= 1;
end if;
end if;
end process;
end architecture behavioural;
......它运作良好(在模拟中),但有一个限制。 send
信号(导致传输开始)必须为'1'电平的时间长于baud_clk
的至少一个完整周期,以便发送器可靠地看到它。
我一直试图找到一种转换此代码的方法,以便它响应send
信号的上升沿而不是在baud_clk
的上升沿测试其电平。我希望能够响应持续时间小于100ns的send
脉冲,即使baud_clk
运行速度慢得多(例如115200 hz)。
我已经尝试过(天真地)改变过程......
shifting_handler:
process(baud_clk) is
begin
if rising_edge(baud_clk) then
if (shifter /= IDLE_BITS) then
shifter <= '0' & shifter(10 downto 1); -- shifter >>= 1;
end if;
elsif rising_edge(send) and (shifter = IDLE_BITS) then
shifter <= "11" & data & '0';
end if;
end process;
在send
没有上升沿时,我希望更改逻辑以测试baud_clk
的上升沿。
我知道这不是解决问题的有效方法(合成器当然会呻吟)但我希望有人能用简单的语言解释为什么不能这样做。如果在一个过程中可以使用两个边缘检测器会发生什么?这里有一个我无法理解的概念,我似乎总是以同样的方式编写代码并产生这个问题。我正在努力应对多年来根深蒂固的软件编程习惯,这无济于事!
答案 0 :(得分:1)
听起来send
与baud_clk
是异步的。因此,您需要执行某种形式的时钟域交叉(CDC)才能正确实现您的设计,否则您的设计将无法通过时序并且可能无法正常运行。 CDC是一个标准术语,您应该能够在其他问题和其他地方找到更多信息。
正如您所发现的,如果对两个不同信号的边缘敏感,则无法在真实硬件中实现设计。没有人没有权利&#39;如何做你想做的事,但这是一个使用简单的“切换”的例子。 CDC。这很简单,但请注意,如果在发送前一个字节之前有一个send
请求到达,设计可能会错过发送一个字节。在send
信号的断言和传输开始之间也会引入一些延迟。目前尚不清楚这些问题在您的系统中是否重要。
创建另一个对send
敏感的流程:
-- The initial state doesn't matter, but we want the design to work in simulation
signal send_toggle : std_logic := '0';
process(send)
begin
if (rising_edge(send)) then
send_toggle <= not send_toggle;
end if;
end process;
现在将另一个进程与baud_clk
域同步。使用两个级联寄存器来产生一种设计,该设计在很大程度上不受任何亚稳态影响(这是您可以查找的另一个标准术语),这可以通过对从不同时钟域生成的信号进行采样而产生:
signal send_toggle_r1 : std_logic;
signal send_toggle_r2 : std_logic;
process(baud_clk)
begin
if (rising_edge(baud_clk)) then
send_toggle_r1 <= send_toggle;
send_toggle_r2 <= send_toggle_r1;
end if;
end process;
以上是一个非常标准的电路块,可以在许多单位CDC场景中使用。
然后,您的传输过程可以注册send_toggle_r2
信号以查找转换,以确定是否应该开始发送。此信号位于正确的时钟域中:
signal send_toggle_r3 : std_logic;
process(baud_clk) is
begin
if rising_edge(baud_clk) then
send_toggle_r3 <= send_toggle_r2;
if ((send_toggle_r3 /= send_toggle_r2) and (shifter = IDLE_BITS)) then
shifter <= "11" & data & '0';
elsif (shifter /= IDLE_BITS) then
shifter <= '0' & shifter(10 downto 1); -- shifter >>= 1;
end if;
end if;
end process;
最后,您需要实现时序约束,以告知您的工具链不要担心send_toggle_r1
寄存器的时序。
您可能会发现,如果您的目标硬件是寄存器的初始状态是随机的,那么在前几个baud_clk
周期之后可能会出现错误的字节传输。为防止出现这种情况,您可以选择在启动后的某些时钟周期内将baud_clk
进程置于重置状态,但由于我不知道这是否与您相关,因此我不会详细说明一部分。
这整个答案直接解决了您的问题,但我个人的方法是使用任何更高速率的时钟生成send
信号来驱动整个设计。然后,串行传输实际上将使用更高速率的时钟,其中通过CDC启用移位。从baud_clk
驱动的边缘检测器链。位时序并不是绝对完美的,但这对于标准的UART&#39;而言并不重要。场景。