本文介绍了VHDL-从电平采样转换为边沿触发-直观的解释?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码(原始的"RS-232信令"发送器)...

I have the following code (a primitive "RS-232 signalling" transmitter)...

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信号(导致传输开始)的电平必须至少在baud_clk个完整周期内为"1"电平,以使发送器可靠地看到它.

... it works well (in simulation) but has a limitation. The send signal (that causes a transmission to begin) has to be a '1' level for longer than at least one full cycle of the baud_clk in order for the transmitter to see it reliably.

我一直试图找到一种方法来转换此代码,以使其响应send信号的上升沿,而不是在baud_clk的上升沿测试其电平.我希望能够响应持续时间少于100ns的send脉冲,即使baud_clk的运行速度要慢得多(例如115200 Hz).

I have been trying to find a way to convert this code so that it responds to the rising edge of the send signal instead of testing its level at the rising edge of baud_clk. I want to be able to respond to a send pulse less than 100ns in duration even when the baud_clk is running at a much slower rate (115200 hz for instance).

我已经尝试过(天真的)改变流程,从而...

I've tried (naively) altering the process thus...

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;

在这里,我希望更改baud_clk上没有上升沿时测试send上的上升沿的逻辑.

Here I was hoping to change the logic to test for a rising edge on send when there isn't a rising edge on baud_clk.

我知道这不是解决问题的有效方法(当然是合成器的mo吟声),但我希望有人可以简单地解释为什么不能做到这一点.如果在一个过程中可以使用两个边缘检测器,将会发生什么?这里有一个我无法理解的概念,而且我似乎总是最终以相同的方式编写代码并产生了这个问题.我正在与多年的根深蒂固的软件编程习惯作斗争,但这并没有多大帮助!

I know that this is not a valid approach to the problem (the synthesizer moans of course) but I was hoping that someone could explain in simple terms why this cannot be done. What would happen if it was possible to use two edge detectors in a process? There is a concept here I cannot grasp and I always seem to end up writing the code in the same way and producing this problem. I'm fighting hard against years of ingrained software programming habits, which doesn't help much!

推荐答案

听起来send相对于baud_clk是异步的.因此,您需要执行某种形式的时钟域穿越(CDC)才能正确实现您的设计,否则您将拥有无法通过时序并有可能无法正确运行的设计. CDC是一个标准术语,您应该能够在其他问题以及其他地方找到更多信息.

It sounds like send is asynchronous with respect to baud_clk. You therefore need to perform some form of clock domain crossing (CDC) in order to correctly implement your design, otherwise you will have a design that cannot pass timing and has the potential to not function correctly. CDC is a standard term that you should be able to find more information about in other questions, and elsewhere.

正如您所发现的,如果设计的过程对两个不同信号的边沿敏感,那么您就无法在真正的硬件中实现设计.没有一种正确"的方式来做您想要的事情,但是这里有一个使用简单的切换" CDC的示例.这非常简单,但是请注意,如果在发送前一个字节之前出现一个send请求,则设计可能会错过发送字节的机会.在发出send信号与传输开始之间还会引入一些延迟.目前尚不清楚这些问题在您的系统中是否重要.

As you have found, you cannot have a design realised in real hardware if it has a process sensitive to edges on two different signals. There's no one 'right' way to do what you want, but here is one example that uses a simple 'toggle' CDC. This is very simple, but note that the design could miss sending a byte if one send request arrives before a previous byte has been transmitted. There will also be some delay introduced between assertion of the send signal, and the transmission starting. It's not clear if these issues matter in your system.

创建另一个对send敏感的进程:

Create another process sensitive to 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域.使用两个级联寄存器来产生一种设计,该设计在很大程度上不受任何亚稳性(这是您可以查询的另一个标准术语)的影响,而亚稳性可能是由于采样了来自不同时钟域的信号而导致的:

Now another process to synchronize this to the baud_clk domain. Use two cascaded registers to produce a design that is largely immune to any metastability (this is another standard term that you can look up) that can result from sampling a signal generated from a different clock domain:

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方案中使用.

The above is a very standard circuit block that you can use in many single-bit CDC scenarios.

然后,您的发送过程可以注册send_toggle_r2信号,以查找过渡,以确定是否应开始发送.该信号在正确的时钟域中:

Your transmit process can then register the send_toggle_r2 signal in order to look for a transition, in order to determine whether it should start sending. This signal is in the correct clock domain:

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寄存器的时序.

Lastly, you will need to implement timing constraints to tell your tool chain not to worry about timing of the send_toggle_r1 register.

您可能会发现,如果针对的是寄存器的初始状态是随机的硬件,则在前几个baud_clk周期后可能会收到错误的字节传输.为了防止这种情况,您可以选择在启动后将baud_clk进程重置为重置某些时钟周期,但是由于我不知道这是否与您相关,因此我将不详细介绍这一部分.

You might spot that if you are targeting hardware where the initial states of registers are random, you might get an erroneous byte transmission after the first few baud_clk cycles. To prevent this, you might choose to hold your baud_clk process in reset for some clock cycles after start up, but as I don't know if this is relevant for you, I won't detail this part.

这整个答案直接解决了您的问题,但是我个人的方法是使用任何速率更高的时钟生成您的send信号来驱动整个设计.这样,串行传输实际上将使用更高速率的时钟,并通过由baud_clk驱动的CDC>边沿检测器链来实现移位.位时序不是绝对完美,但是对于标准的"UART"场景而言,这无关紧要.

This whole answer addresses your question directly, but my personal approach would be to use whatever higher-rate clock is generating your send signal to drive the entire design. The serial transmission would then in fact use the higher rate clock, with shifting enabled by a CDC > edge detector chain driven from the baud_clk. The bit timing would not be absolutely perfect, but this should not matter for a standard 'UART' scenario.

这篇关于VHDL-从电平采样转换为边沿触发-直观的解释?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-21 11:59