Genere flip-flops usando solo lógica combinacional

Solo por diversión, quería diseñar y simular flip-flops tipo D usando solo lógica combinacional en Verilog (o SystemVerilog). Estoy usando Verilator para la simulación.

Mi intento inicial, que usa un diseño clásico de seis NAND, parece funcionar bien y ha pasado todas las pruebas. Mi segundo intento, basado en un tipo JK de cuatro NAND, no funciona. La salida no permanece bloqueada durante el nivel positivo del reloj y, para algunas pruebas, la simulación ni siquiera converge.

P: Sé que no es normal ni óptimo, pero ¿es razonable diseñar flip-flops usando lógica combinacional en Verilog? Si es así, ¿hay algún problema con mi segundo diseño?

Este funciona:

module dff( input clk, input D, output Q );

   wire a, sn, rn, b, Qn;

always_comb // captures D @( posedge clk )
   begin
   a = !(b&sn);
   sn = !(a&clk);
   rn = !(sn&b&clk);
   b = !(rn&D);
   Q = !(sn&Qn);
   Qn = !(rn&Q);
   end

endmodule

Este no funciona:

module dff( input clk, input D, output Q );

   wire J=D, K=!D;
   wire sn, rn, qn;

always_comb // captures D @( posedge clk ), but fails to hold
   begin
   sn = !(J&clk&qn);
   rn = !(K&clk&Q);
   Q  = !(sn&qn);
   qn = !(rn&Q);
   end

endmodule

En un esfuerzo por eliminar la asignación de bloqueo y las listas de sensibilidad , volví a implementar el enfoque basado en JK de la siguiente manera, pero las formas de onda de salida no se vieron afectadas por esta diferencia.

module dff( input clk, input D, output Q );

   wire J=D, K=!D;
   wire sn, rn, qn;

   assign sn = !(J&clk&qn);
   assign rn = !(K&clk&Q);
   assign Q  = !(sn&qn);
   assign qn = !(rn&Q);

endmodule

Nota: basé el diseño de estos en las descripciones y diagramas aquí .

Puede usar HDL para hacer esto, pero eso solo tiene sentido para los ASIC. Los FPGA no tienen las características de retardo adecuadas para ser útiles en la construcción de circuitos secuenciales que utilizan rutas de retroalimentación combinatorias.

Respuestas (2)

El JK que tiene es un pestillo, no un disparador de borde. También falta la retroalimentación entre Q y qn (a su primer código también le falta esta retroalimentación). Combinacional siempre bloquea el trabajo en función de la lista de sensibilidad. La lista de sensibilidad automática de always @*y always_comb` está determinada por las señales que se usan en el lado derecho de una expresión y no en el lado izquierdo.

Si declara la lista de sensibilidad, debería funcionar: always @( J,K,clk, Q,qn )de esta manera, Q y qn volverán a activar el bloqueo siempre.

Otro enfoque es usar una declaración de caso:

always_comb
  if (clk)
    Q = Qpre;
  else
    case({J,K})
    2'b10 : Qpre = 1'b1;
    2'b01 : Qpre = 1'b0;
    2'd11 : Qpre = ~Q;
    default : Qpre = Qpre; // no change
    endcase

El problema con el uso de la lógica combinacional para los fracasos es que puede haber violaciones del tiempo de espera. Verilog un simulador indeterminado. Esto significa que no se garantiza el orden en que se evalúa un bloque siempre. Usando el siguiente código como ejemplo. Verilog puede ejecutarse b_combffantes c_combff, por lo que cse le asignará el valor de a. Esto se debe a que estamos utilizando asignaciones de bloqueo. El simulador también podría ejecutarse c_combffantes b_combffy obtendremos el valor correcto. Ambos escenarios son legales.

dff b_combff(.Q(b), .clk(clk), .D(a);
dff c_combff(.Q(c), .clk(clk), .D(b);

Una asignación sin bloqueo bien ubicada ( <=) en Q puede ayudar. Esto separará la evaluación y la actualización en regiones separadas del programador. Esto corrige la condición de carrera del tiempo de espera pero no ayuda con la síntesis.

Los sintetizadores modernos son inteligentes, pero no son brillantes. Buscan patrones de codificación para determinar cómo convertir RTL en puertas equivalentes. Cuando lo vean, always @se convertirán en chanclas sensibles a los bordes. Funcionan bien con pestillos SR, RS y D muy simples. Biond que tratan de hacer lógica combinacional. Cuando agrega sus propios fracasos personalizados, el sintetizador intentará hacerlo coincidir con la lógica que ya conoce, lo que probablemente dará como resultado una lógica de bloqueo asincrónico.

En resumen, es posible diseñar flip-flops usando lógica combinacional, pero fuera del aprendizaje generalmente no es una buena idea.

Gracias por las explicaciones, la mayor parte de eso tiene sentido para mí. Sin embargo, no entiendo por qué dices que implementé un pestillo en lugar de un flip-flip: modelé la lógica desde here , donde se indica que se activa por el borde. Además, no veo cómo me estoy perdiendo los comentarios de Q/qn. ¿ No debería always_combinferir que la lista de sensibilidad son todas las variables de las declaraciones contenidas? ¿Quizás está insinuando que las asignaciones de bloqueo son el único problema, y ​​los problemas de enganche/retroalimentación se derivan de eso?
Finalmente entiendo lo que decías acerca de que mi JK no se activa por el borde (ver mi respuesta). Todavía no sé qué quiso decir con "retroalimentación faltante entre Q y qn". Parece que tengo esa retroalimentación en mi lógica, y funciona como debería.
Dentro de un orden de principio a fin de los asuntos de operación. Si cambia el orden, podrá ver un comportamiento extraño. Por ejemplo, asigne 'Q' antes de 'sn' en el bloque siempre.
Veo a lo que te refieres, pero no estoy de acuerdo. Mis pruebas muestran que no hay dependencias de orden (ejecutándose bajo Verilator). Este es un tema interesante, y publiqué una respuesta detallada como respuesta a otra pregunta: aquí .
Ok, creo que lo entiendo ahora, lo siento, solo me estoy poniendo al día con estas cosas. Sus comentarios sobre el orden de operación están asumiendo el estilo de software o la simulación previa a la síntesis . Anteriormente no había considerado esa posibilidad porque no es así como funciona Verilator (o el hardware real). Esta distinción de la semántica de Verilog se describe en la sección 2.2 del documento aquí . Me volví loco y pregunté más sobre esta distinción aquí .
Ah, Varilator se ejecuta como si fuera una síntesis posterior y, por lo tanto, no se adhiere estrictamente a los estándares Verilog/SystemVerilog. Eso explica el comportamiento. También debería haber recibido errores al asignar wiretipos en un bloque siempre. Cliff escribe grandes artículos; Me refiero a ellos también cuando me enfrento a desafíos más allá de LRM. El IEEE Std 1800-2012 es la última especificación oficial de SystemVerilog. Y para su información: también puede probar otros simuladores en EDAplayground .

Las descripciones del flip-flop JK tienden a ser muy confusas. En particular, la interacción con el reloj rara vez parece describirse de una manera completamente coherente, pero esto puede deberse en gran medida a un problema mayor de uso de terminología impreciso y ambiguo .

El problema básico con el flip-flop JK de la pregunta se describe en esta respuesta . En resumen, lo que se modela no se activa por el borde, a pesar de las indicaciones de lo contrario en el artículo de Wikipedia al que se hace referencia .

Para construir un flip-flop JK activado por borde, puede usar una configuración de dos etapas de pestillos en cascada (lo que se denomina "maestro-esclavo"), de modo que las dos etapas estén en estado transparente frente a estado de espera durante fases de reloj opuestas. . En este arreglo, la primera etapa mantendrá los datos estables mientras que la segunda etapa está en su estado transparente. Esta relación crea el efecto edge-trigger y se implementa a continuación...

module dff( input clk, input D, output Q );
   wire Q1;
   jkff A(!clk,D,!D,Q1);
   jkff B(clk,Q1,!Q1,Q);
endmodule

...dónde jkffestá la misma lógica que antes (pero sería mejor llamarlo "cerrojo cerrado JK")...

module jkff( input clk, input J, input K, output Q );

   wire sn, rn, qn;

always_comb
   begin
   sn = !(J&clk&qn);
   rn = !(K&clk&Q);
   Q  = !(sn&qn);
   qn = !(rn&Q);
   end

endmodule

La falta de convergencia informada puede explicarse por el modo de alternar no convergente del diseño activado por nivel, si se permitiera que las entradas mantuvieran el estado (J=K=1).

De mayor interés: El libro "Verilog HDL", de Samir Palnitkar, incluye una implementación de un flip-flop tipo D que es prácticamente equivalente al que se muestra en la pregunta.

Entonces, para ser explícito: la respuesta a la pregunta inicial es: sí, esto es algo razonable de hacer. Sin embargo, no hay garantía de que algún hardware en particular pueda realizar tales diseños. Los diferentes simuladores también pueden dar resultados diferentes, especialmente con respecto a la distinción entre simulación previa y posterior a la síntesis.