CPLD (a veces) no incrementa el contador

4

Tengo este programa simple ejecutándose en el Altera EPM240 que a veces no hace el incremento del contador.

reg trigger_state;
reg [7:0] trigger_count;

always @ (posedge clk)
begin
    if (trigger != trigger_state) begin
      trigger_state <= !trigger_state;
      trigger_count <= trigger_count + 1;
    end
end

clk se ejecuta a 50 MHz, y la señal de disparo en la imagen a 1-2 kHz.

En la imagen ( trigger=6 , trigger_state=4 , trigger_count[1]=1 , trigger_count[0]=2 )

Como puede ver, trigger_state siempre sigue la señal de entrada ( trigger ), pero el contador ( 2 ) a veces no se incrementa.

¿Qué podría explicar tal comportamiento?

    
pregunta rumpel

3 respuestas

6

El puño de todos:
El disparador se está sincronizando con el reloj. Primero debe sincronizarlo antes de poder usarlo de manera segura. El código para eso es:

reg sync_trigger,safe_trigger;
always @(posedge clk)
begin
    sync_trigger <= trigger;
    safe_trigger <= sync_trigger;
end

El comportamiento que estás viendo es porque el hardware se verá así:

if (trigger != trigger_state) 
  trigger_state <= !trigger_state;

if (trigger != trigger_state) 
  trigger_count <= trigger_count + 1;

Como el disparador es sincrónico, puede tener uno si es verdadero y el otro es falso. Así se actúa uno y el otro no. De hecho, se implementa para cada BIT de su contador, por lo que algunos bits pueden aumentar y otros no.

Publicar pensamiento:
¡Su disparador debe ser más lento que su reloj, de lo contrario perderá los disparadores!

    
respondido por el Oldfart
9

¿La entrada trigger es completamente asíncrona al reloj de 50MHz? Si es así, es probable que viole los requisitos de configuración y tiempo de espera de vez en cuando. Debe sincronizarlo con el reloj a través de al menos dos FF antes de usarlo en cualquier toma de decisiones.

Puedo escucharte diciendo: "Pero solo se usa en una declaración if . Si se actualiza trigger_state , ¿no garantiza eso que también se actualiza trigger_count ?"

La respuesta es "no".

trigger_state y trigger_count representan un total de nueve FF separados, y cada uno de ellos tiene su propia lógica combinatoria para determinar cuál será su próximo estado. Cada uno toma trigger como una entrada, y cada uno toma una decisión independiente sobre cuál es el valor de trigger . Si viola los requisitos de configuración / tiempo de espera, ya no puede garantizar que esas decisiones independientes sean coherentes, lo que llevará al comportamiento que observa.

Tenga en cuenta que esto no tiene nada que ver con la metastabilidad, aunque la sincronización de entradas asíncronas también mitiga ese problema.

    
respondido por el Dave Tweed
4

Lo más probable es que el problema sea la metaestabilidad causada por el uso de una señal asíncrona (su entrada trigger ) para controlar su contador.

Al introducir cualquier señal, debe asegurarse de que esté sincronizada con la señal del reloj mediante una cadena de sincronización de registro múltiple. Algo como:

reg [1:0] triggerSync;
always @ (posedge clk) begin
    triggerSync <= {triggerSync[0],trigger}
end

Luego usa triggerSync[1] en tu código. El primer registro triggerSync[0] sincronizará la señal, pero puede ser metaestable si la entrada cambia a medida que cambia el reloj (configuración / violación de retención). El segundo registro triggerSync[1] atrapa el estado metaestable y detiene su propagación por el resto de su lógica, lo que causa errores extraños.

Tu lógica en sí misma también parece extraña. Yo sugeriría dividirlo en su lógica de detección de bordes, y su contador:

reg triggerDly;
wire triggerEdge;
always @ (posedge clk) begin
    triggerDly <= triggerSync[1];
end
assign triggerEdge = (triggerDly != triggerSync[1]);

always @ (posedge clk) begin
    if (triggerEdge) begin
        trigger_count <= trigger_count + 1;
    end
end

La división de todo hace que el código sea más fácil de seguir y depurar. También puede modificar fácilmente la lógica del detector de bordes para aumentar el borde, el borde descendente o ambos. También puede hacer que el detector de bordes sea un submódulo, ya que es un bit de código útil para reutilizar.

    
respondido por el Tom Carpenter

Lea otras preguntas en las etiquetas