VHDL - converting from level sampling to edge triggered - an intuitive explanation?

巧了我就是萌 提交于 2019-12-11 06:14:12

问题


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;

... 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.

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;

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.

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!


回答1:


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.

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.

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;

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;

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

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;

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

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.

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.



来源:https://stackoverflow.com/questions/43682061/vhdl-converting-from-level-sampling-to-edge-triggered-an-intuitive-explanati

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!