9 votos

VHDL: recibir módulo al azar cuando falla el conteo de bits

De fondo

Este es un proyecto personal; se refiere a la conexión de un FPGA para un N64, los valores de bytes que el FPGA recibe luego son enviados a través de la UART para mi equipo. Que funciona realmente muy bien! A veces al azar por desgracia, el dispositivo va a fallar, luego se recupera. A través de la depuración, me las he arreglado para encontrar el problema, sin embargo, estoy perplejo en cómo solucionarlo porque estoy bastante incompetente con VHDL.

He estado jugando con el VHDL para un par de días y me puede ser incapaz de resolver esto.

El Problema

Tengo un osciloscopio medir la N64 de la señal en la FPGA, y el otro canal se conecta a la salida de la FPGA. También tengo los pines digitales de grabación el valor del contador.

Esencialmente, la N64 envía 9 bits de datos, incluyendo un bit de PARADA. El contador cuenta los bits de datos recibidos y cuando llego a 9 bits, el FPGA empieza a transmitir a través de UART.

Aquí el comportamiento correcto: enter image description here

El FPGA es la forma de onda azul y el naranja de la forma de onda es la entrada de la N64. Para la duración de la recepción, mi FPGA "echos" la señal de la entrada para propósitos de depuración. Después de la FPGA cuenta a 9, comienza la transmisión de datos a través de la UART. Observe que los pines digitales contar hasta 9 y el FPGA salida pasa a nivel BAJO, inmediatamente después de la N64 es terminado.

He aquí un ejemplo de un fracaso:

enter image description here

Aviso de que el contador se salta los bits 2 y 7! El FPGA llega a la final, a la espera para el siguiente bit de inicio de la N64, pero nada. Así que la FPGA y se recupera.

Este es el VHDL para el N64 recibir módulo. Contiene el contador: s_bitCount.

library IEEE;
use IEEE.STD_LOGIC_1164.all;   
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity N64RX is
     port(
         N64RXD : in STD_LOGIC;                    --Data input
         clk25 : in STD_LOGIC;
         clr : in STD_LOGIC; 
         tdre : in STD_LOGIC;                      --detects when UART is ready
         transmit : out STD_LOGIC;                 --Signal to UART to transmit  
         sel : out STD_LOGIC; 
         echoSig : out STD_LOGIC;
         bitcount : out STD_LOGIC_VECTOR(3 downto 0);
         data : out STD_LOGIC_VECTOR(3 downto 0)   --The significant nibble
         );
end N64RX;

--}} End of automatically maintained section

architecture N64RX of N64RX is 

type state_type is (start, delay2us, sigSample, waitForStop, waitForStart, timeout, count9bits, sendToUART);

signal state: state_type;
signal s_sel, s_echoSig, s_timeoutDetect : STD_LOGIC;
signal s_baudCount : STD_LOGIC_VECTOR(6 downto 0);  --Counting variable for baud rate in delay
signal s_bitCount : STD_LOGIC_VECTOR(3 downto 0);  --Counting variable for number of bits recieved 
signal s_data : STD_LOGIC_VECTOR(8 downto 0);   --Signal for data

constant delay : STD_LOGIC_VECTOR(6 downto 0) := "0110010";  --Provided 25MHz, 50 cycles is 2us 
constant delayLong : STD_LOGIC_VECTOR(6 downto 0) := "1100100";

begin 

n64RX: process(clk25, N64RXD, clr, tdre)
begin
    if clr = '1' then
        s_timeoutDetect <= '0';
        s_echoSig <= '1';
        s_sel <= '0';
        state <= start;
        s_data <= "000000000";
        transmit <= '0'; 
        s_bitCount <= "0000";
        s_baudCount <= "0000000";  
    elsif (clk25'event and clk25 = '1') then    --on rising edge of clock input
        case state is
            when start =>   
                --s_timeoutDetect <= '0';
                s_sel <= '0';
                transmit <= '0';        --Don't request UART to transfer   
                s_data <= "000000000";
                s_bitCount <= X"0";   
                if N64RXD = '1' then
                    state <= start;
                elsif N64RXD = '0' then     --if Start bit detected
                    state <= delay2us;
                end if;    

            when delay2us =>                 --wait two microseconds to sample
                --s_timeoutDetect <= '0';
                s_sel <= '1';
                s_echoSig <= '0';
                if s_baudCount >= delay then    
                    state <= sigSample;
                else
                    s_baudCount <= s_baudCount + 1;
                    state <= delay2us;
                end if;  

            when sigSample => 
                --s_timeoutDetect <= '1';
                s_echoSig <= N64RXD;
                s_bitCount <= s_bitCount + 1;
                s_baudcount <= "0000000";
                s_data <= s_data(7 downto 0) & N64RXD;      
                state <= waitForStop;   

            when waitForStop => 
                s_echoSig <= N64RXD;
                if N64RXD = '0' then
                    state <= waitForStop;
                elsif N64RXD = '1' then
                    state <= waitForStart;
                end if;   

            when waitForStart => 
                s_echoSig <= '1';
                s_baudCount <= s_baudCount + 1; 
                if N64RXD = '0' then 
                    s_baudCount <= "0000000";
                    state <= delay2us;
                elsif N64RXD = '1' then 
                    if s_baudCount >= delayLong then
                        state <= timeout;
                    elsif s_bitCount >= X"9" then
                        state <= count9bits;
                    else
                        state <= waitForStart;
                    end if;
                end if;     

            when count9bits =>  
                s_sel <= '0';
                if tdre = '0' then
                    state <= count9bits;
                elsif tdre = '1' then
                    state <= sendToUART;
                end if;   

            when sendToUART =>
                transmit <= '1';
                if tdre = '0' then
                    state <= start;
                else
                    state <= sendToUART;
                end if;

            when timeout =>
                --s_timeoutDetect <= '1';
                state <= start;

        end case;   
    end if;
end process n64RX;  
--timeoutDetect <= s_timeoutDetect;
bitcount <= s_bitCount;
echoSig <= s_echoSig;
sel <= s_sel;
data <= s_data(4 downto 1);

end N64RX;

Así que, ¿alguna idea? Sugerencias de depuración? Consejos sobre la codificación de Máquinas de Estado Finito?

Mientras tanto, voy a seguir jugando con ella (voy a tener que eventualmente)! Me ayudan Intercambio de la Pila, tú eres mi única esperanza!

Editar

Un descubrimiento en mi depuración, los estados va a saltar de waitForStart de vuelta a waitForStop. Me dio a cada estado un valor con waitForStart igual a '5' y waitForStop igual a '4'. Ver la imagen de abajo: enter image description here

9voto

Mark Puntos 1998

No veo un sincronizador en la rx de línea de datos.

Todos asincrónica entradas deben ser sincronizado con el reloj de muestreo. Hay un par de razones para esto: metaestabilidad y de enrutamiento. Estos son problemas diferentes pero relacionados entre sí.

Se necesita tiempo para que las señales se propagan a través de la FPGA de la tela. El reloj de la red dentro de la FPGA está diseñado para compensar estos "viajes" retrasos " de modo que todos los flip flops dentro de la FPGA ver el reloj en el mismo momento. La normal de enrutamiento de red no dispone de este, y en su lugar se basa en la regla de que todas las señales deben ser estables para un poco de tiempo antes de que el reloj de los cambios y permanecer estable durante un poco de tiempo después de los cambios de reloj. Estos pequeños trozos de tiempo se conoce como el programa de instalación y los tiempos de espera para un determinado flip flop. El lugar y la ruta de los componentes de la cadena de herramientas tiene una muy buena comprensión de los retrasos en el direccionamiento para el dispositivo específico y hace una suposición básica de que una señal de no violar el programa de instalación y los tiempos de espera de las chanclas en la FPGA. Con la asunción y el conocimiento (y una falta de tiempo de archivo) se puede colocar adecuadamente la lógica dentro de la FPGA y asegurarse de que toda la lógica que se ve a una señal dada ve el mismo valor en cada tic de reloj.

Cuando haya señales que no están sincronizados con el reloj de muestreo puede acabar en la situación en que un flip flop ve el "viejo" valor de una señal desde el nuevo valor no ha tenido tiempo para propagar más. Ahora estás en la situación indeseable en el que la lógica de mirar la misma señal se ve a dos valores diferentes. Esto puede causar el mal funcionamiento, se estrelló el estado de las máquinas y de todo tipo de duro para diagnosticar estragos.

La otra razón por la que usted debe sincronizar todas las señales de entrada es algo que se llama metaestabilidad. Hay volúmenes escritos sobre este tema, pero en pocas palabras, circuitos de lógica digital está en su nivel más básico de un circuito analógico. Cuando el reloj de la línea se eleva el estado de la línea de entrada es capturado y si que la entrada no es un estable nivel alto o bajo en ese momento, un desconocido "entre" valor puede ser capturado por el muestreo flip flop.

Como ustedes saben, los FPGAs son bestias digitales y no reaccionan bien a una señal que no es ni alto ni bajo. Peor, si que indeterminada valor hace su camino más allá de la toma de muestras flip flop y en la FPGA puede provocar todo tipo de barbaridades como la mayor parte de la lógica de ver ahora un valor indeterminado y tratar de hacer sentido de ella.

La solución es para sincronizar la señal. En su nivel más básico, esto significa que usted utilice una cadena de flip flops para capturar la entrada. Cualquier metaestable nivel que podría haber sido capturado por el primer flip flop y logró consigue sacar otra oportunidad para ser resuelto antes de que llegue a su lógica compleja. Dos flip flops generalmente son más que suficientes para sincronizar las entradas.

Un básico sincronizador se parece a esto:

entity sync_2ff is
port (
    async_in : in std_logic;
    clk : in std_logic;
    rst : in std_logic;
    sync_out : out std_logic
);
end;

architecture a of sync_2ff is
begin

signal ff1, ff2: std_logic;

-- It's nice to let the synthesizer know what you're doing. Altera's way of doing it as follows:
ATTRIBUTE altera_attribute : string;
ATTRIBUTE altera_attribute OF ff1 : signal is "-name SYNCHRONIZER_IDENTIFICATION ""FORCED IF ASYNCHRONOUS""";
ATTRIBUTE altera_attribute OF a : architecture is "-name SDC_STATEMENT ""set_false_path -to *|sync_2ff:*|ff1 """;

-- also set the 'preserve' attribute to ff1 and ff2 so the synthesis tool doesn't optimize them away
ATTRIBUTE preserve: boolean;
ATTRIBUTE preserve OF ff1: signal IS true;
ATTRIBUTE preserve OF ff2: signal IS true;

synchronizer: process(clk, rst)
begin
if rst = '1' then
    ff1 <= '0';
    ff2 <= '0';
else if rising_edge(clk) then
    ff1 <= async_in;
    ff2 <= ff1;
    sync_out <= ff2;
end if;
end process synchronizer;
end sync_2ff;

Conecte el físico pin para el N64 del controlador de rx de línea de datos a la async_in entrada del sincronizador, y conecte el sync_out señal a su UART del rxd entrada.

Sin sincronizar las señales puede causar extraño problemas. Asegúrese de que cualquier entrada conectado a un FPGA elemento que no está sincronizado con el reloj del proceso de lectura de la señal de sincronización. Esto incluye pulsadores, UART 'rx' y 'cts' señales... cualquier cosa que no está sincronizado con el reloj de la FPGA está utilizando para el muestreo de la señal.

(Un aparte: yo escribí la página en www.mixdown.ca/n64dev hace muchos años. Me acabo de dar cuenta que se me rompió el enlace de la última vez que me actualizado el sitio, y fijará en la mañana cuando estoy en un equipo. Yo no tenía idea de lo que muchas personas usan esa página!)

6voto

GSerg Puntos 33571

Su problema es que usted está utilizando señales no sincronizadas para tomar decisiones en su estado de la máquina. Usted debe ser la alimentación de todas esas señales externas a través de la doble FF sincronizadores antes de usarlos en el estado de la máquina.

Es un problema sutil con máquinas de estado que pueden surgir en cualquier estado de transición que implica un cambio a dos o más bits de la variable de estado. Si utiliza una desincronización de entrada, uno de los bits que se puede cambiar mientras que la otra no para de cambiar. Esto le lleva a un estado diferente del previsto, y que podría o no ser un estado de derecho.

Esa última declaración es la razón por la que usted también debe tener siempre un caso de incumplimiento (en VHDL, when others => ...) en su estado de la máquina case, que lleva desde cualquier estatal ilegal a legal.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X