Conduciendo la máquina de estado en FPGA directamente desde la entrada

1

Estoy aprendiendo Verilog y en el proceso tratando de implementar algunos circuitos lógicos simples (y como todos, un SOC simple)

Implementé un UART simple, y funcionó bien durante la simulación y los bancos de prueba, pero después de la síntesis en un FPGA real (un ice40, usando Yosys), me encuentro con problemas en aproximadamente el 2% de los caracteres recibidos. / p>

Usando un analizador lógico, capturé la siguiente traza:

D2sonlosdatosdeRX,D3sonlosdatosdeTX(debenserunecodelosdatosrecibidos),D0sonlospulsosdemuestreodelUART,D1esunaseñalde"recepción de uart".

El problema fue que el UART estaba terminando antes el byte actual ha terminado.

Después de muchas pruebas, me di cuenta de que cuando la transición de Alto a Bajo en el bit de inicio estaba lo suficientemente cerca del reloj FPGA interno, el estado inicial en verilog no estaba configurado en el valor correcto.

El código de verilog era:

    always @(posedge clk)
    begin
         // Main RX processing
         if (!rx_active)
         begin
             if (!rx)
             begin
                 rx_shift <= 0;
                 rx_state <= 19;
             end
         end

Cambié el código a:

    always @(posedge clk)
    begin
         // Latches RX signal, prevents glitches
         rx_latch <= rx;

         // Main RX processing
         if (!rx_active)
         begin
             if (!rx_latch)
             begin
                 rx_shift <= 0;
                 rx_state <= 19;
             end
         end

Después de ese cambio, el UART funcionó siempre bien, esto significa que la lógica combinatoria que genera el estado de transición estaba configurando solo algunos de los bits en rx_state, ya que probablemente la señal del reloj no alcanzó todos los FF al mismo tiempo.

Mis preguntas son:

  • ¿Es este comportamiento esperado en Verilog?
  • ¿Cómo puedo probar si estas condiciones de carrera son posibles en mi código?
pregunta dmsc

1 respuesta

0

El problema es que está sincronizando una señal asíncrona (rx) en varios registros (rx_shift y rx_state) al mismo tiempo. Si la señal de entrada hace una transición cerca del reloj, puede leerse como 0 o 1, dependiendo de los retrasos particulares desde la entrada asíncrona a la entrada del registro.

La adición de un registro adicional para sincronizar la entrada asíncrona (rx_latch en su caso) se llama sincronización y garantiza que los registros internos vean el mismo valor para la señal externa.

Tenga en cuenta que a velocidades de reloj muy altas, puede encontrar problemas de metastability , pero para Las tasas de UART no deberían ser un problema.

    
respondido por el crj11

Lea otras preguntas en las etiquetas