Receptor UART BHD de VHDL

1

Estoy tratando de hacer funcionar un receptor UART simple y estoy cerca, pero recibo valores ligeramente off (algunos bits se desplazan un par de lugares desde donde deberían estar).

Basado en esta respuesta. Intenté ejecutar RX a través de un flip flop (no estoy seguro de haberlo hecho). que correctamente) y sobreexplotado RX. Mi tasa de baudios objetivo es 2500000, por lo que es de 400 nudos por baudio y 20 muestras por baudio (el reloj FPGA es de 50 MHz).

¿Alguien puede ver lo que estoy haciendo mal? ¿Estoy poniendo RX en el flip flop y haciendo un sobremuestreo correctamente?

entity uart is port(
    clk_50 : in std_logic;
    reset : in std_logic;
    tx : out std_logic;
    rx : in std_logic;
    data_in : out std_logic_vector(7 downto 0); -- data being received
    data_out : in std_logic_vector(7 downto 0); -- data being sent
    leds    : out std_logic_vector(7 downto 0));    
end uart;

architecture behavioral of uart is

signal sample_counter : unsigned(7 downto 0);
signal data_count : unsigned(3 downto 0);
type uart_state_t is (IDLE, START, DATA, STOP, STALL);
signal uart_state: uart_state_t;
signal next_uart_state: uart_state_t;

signal rx_data : std_logic_vector(7 downto 0);

signal rx_d : std_logic;

begin

process(clk_50, reset)
begin
    if (reset = '1') then
        uart_state <= START;
        data_count <= (others => '0');
        sample_counter <= (others => '1');
        leds <= (others => '0');
        rx_data <= (others => '0');

    elsif (rising_edge(clk_50)) then
        uart_state <= next_uart_state;
        rx_d <= rx;

        if (next_uart_state = START) then
            data_count <= (others => '1');

            if (sample_counter = x"ff") then
                if (rx_d = '0') then
                    sample_counter <= sample_counter + 1;
                end if;
            elsif (sample_counter = x"31") then -- 20 samples per baud
                    sample_counter <= (others => '1');
            else
                    sample_counter <= sample_counter + 1;
            end if;
        end if;             
        elsif (next_uart_state = DATA) then         
            if (sample_counter = x"0a") then    -- check rx at sample 10
                rx_data(to_integer(data_count)) <= rx_d;
                leds(to_integer(data_count)) <= rx_d;
                sample_counter <= sample_counter + 1;
            elsif (sample_counter = x"13") then
                sample_counter <= (others => '1');
                data_count <= data_count + 1;
            else
                sample_counter <= sample_counter + 1;
            end if;

        elsif (next_uart_state = STOP) then
            if (sample_counter = x"0a") then    -- check rx at sample 10
                data_in <= rx_data;
                sample_counter <= sample_counter + 1;
            elsif (sample_counter = x"13") then
                sample_counter <= (others => '1');
            else
                sample_counter <= sample_counter + 1;
            end if;
        end if;
    end if;
end process;

process(uart_state, reset, data_count, sample_counter)
begin
    if (reset = '1') then
        next_uart_state <= START;
    end if;
    case uart_state is
    when START =>
        if (sample_counter = x"13") then
            next_uart_state <= DATA;
        else
            next_uart_state <= START;
        end if;
    when DATA =>
        if (data_count = x"8" and sample_counter = x"13") then
            next_uart_state <= STOP;
        else
            next_uart_state <= DATA;
        end if;
    when STOP =>
        if (sample_counter = x"13") then
            next_uart_state <= START;
        else
            next_uart_state <= STOP;
        end if;
    when others =>
        next_uart_state <= START;
    end case;
end process;
end behavioral;

e: Creo que lo conseguí: reduje la velocidad de transmisión a 1000000 y cambié la lógica para el estado de INICIO.

    
pregunta user693861

1 respuesta

1

Su edición no tiene ningún sentido: ha cambiado el tiempo para el bit de inicio, pero no para ninguno de los otros bits. Además, solo lo modificaste para el proceso cronometrado, pero no el proceso desbloqueado que realmente determina el valor de next_uart_state .

El problema principal con su código original son sus contadores. Por alguna razón, sigues restableciéndolos a todos en lugar de ceros. Esto agrega un estado adicional a cada secuencia de conteo.

Por ejemplo, su sample_count se ejecuta desde 0xFF (-1) hasta 0x13 (+19), que es un total de 21 estados, no 20 como implican sus comentarios. Esto crea un error del 5% en la frecuencia de muestreo, que es suficiente para crear un problema después de que se hayan recibido uno o dos bytes.

Además, su código espera el período completo del bit de parada antes de volver a buscar el siguiente bit de inicio, lo que significa que nunca puede "ponerse al día". En su lugar, debe verificar el valor del bit de parada a la mitad, y luego inmediatamente volver al estado START para buscar el borde inicial del siguiente bit de inicio. Si hace esto, entonces incluso si el reloj de recepción es un poco lento con respecto al reloj de transmisión, podrá mantenerse al día.

Finalmente, he visto muchas formas de codificar máquinas de estado en HDL, pero esta realmente se lleva la torta. ¿Por qué la carcasa del proceso cronometrado en next_uart_state en lugar de uart_state ? Esto crea una gran cantidad de casos de límites extraños que confundirán a cualquiera que lea este código, y creo que incluso te has confundido.

    
respondido por el Dave Tweed

Lea otras preguntas en las etiquetas