CPLD (a veces) no incrementa el contador

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

clkestá funcionando a 50 MHz, y la señal de activación en la imagen a 1-2 kHz.

Ingrese la descripción de la imagen aquí

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

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

¿Qué podría explicar tal comportamiento?

¿De dónde viene "Trigger"? ¿Es de una señal externa o un interruptor? Si es así, ¿le has puesto alguna sincronización?

Respuestas (3)

En primer lugar:
su gatillo viene de forma asincrónica 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á viendo se debe a que el hardware se parecerá más a esto:

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

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

Como el disparador es asincrónico, puede tener ese si es verdadero y el otro es falso. Así, uno está enfadado y el otro no. De hecho, se implementa para cada BIT de su contador, por lo que algunos bits pueden incrementarse y otros no.

Pensamiento posterior: ¡
Tu gatillo debe ser más lento que tu reloj, de lo contrario perderás los gatillos!

¿Qué pasa con el almacenamiento de la señal externa en un pestillo SR para evitar perder el gatillo?

¿ La triggerentrada es completamente asíncrona con el reloj de 50 MHz? Si es así, probablemente infrinja 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 para tomar cualquier decisión.

Puedo escucharlo decir: "Pero solo se usa en una ifdeclaración. Si trigger_statese actualiza, ¿no garantiza eso que trigger_counttambién se actualice?"

La respuesta es no."

trigger_statey trigger_countrepresentan un total de nueve FF independientes, y cada uno de ellos tiene su propia lógica combinatoria para determinar cuál será su próximo estado. Cada uno toma triggercomo entrada, y cada uno toma una decisión independiente sobre cuál triggeres el valor de. Si viola los requisitos de tiempo de configuración/espera, ya no puede garantizar que esas decisiones independientes sean consistentes, lo que conducirá al comportamiento que observa.

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

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 traer cualquier señal, debe asegurarse de que esté sincronizada con la señal del reloj mediante una cadena sincronizadora de registros múltiples. 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 volverse metaestable si la entrada cambia a medida que cambia el reloj (violación de configuración/retención). El segundo registro triggerSync[1]capta el estado metaestable y evita que se propague por el resto de la lógica, lo que provoca fallas extrañas.

Tu lógica en sí también parece rara. Sugeriría dividirlo en la lógica de su detector 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

Dividir 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 borde para que sea de borde ascendente, borde descendente o ambos. También podría hacer que el detector de bordes sea un submódulo, ya que es un código útil para reutilizar.

Básicamente, es imposible observar eventos de metaestabilidad a 50 MHz con tecnología moderna a menos que tenga un diseño congestionado sin holgura. Como explica Dave, probablemente se trate de un problema de coherencia. La solución es la misma (sincronizar FF), pero el problema es diferente.