VHDL - 从水平采样转换为边缘触发 - 直观的解释?

时间:2017-04-28 14:00:07

标签: logic vhdl simulation waveform synthesis

我有以下代码(原始的“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的上升沿。

我知道这不是解决问题的有效方法(合成器当然会呻吟)但我希望有人能用简单的语言解释为什么不能这样做。如果在一个过程中可以使用两个边缘检测器会发生什么?这里有一个我无法理解的概念,我似乎总是以同样的方式编写代码并产生这个问题。我正在努力应对多年来根深蒂固的软件编程习惯,这无济于事!

1 个答案:

答案 0 :(得分:1)

听起来sendbaud_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;而言并不重要。场景。