问题描述
我有以下代码(原始的"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-从电平采样转换为边沿触发-直观的解释?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!