Usando código VHDL para diseñar un JK Flip Flop

Estoy usando quartus II para diseñar un JK Flip Flop. Sin embargo, mis resultados muestran una salida desconocida. ¿Por qué es?

Circuito de diseño previsto:

ingrese la descripción de la imagen aquí

Código VHDL:

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

entity JKFlipFlopGate is
    port(
        J,K,Clk : in std_logic;             --JK Flip-Flop gate input 1 & 2
        Q,Qbar : out std_logic              --JK Flip-Flop gate output
    );
end JKFlipFlopGate;

architecture result of JKFlipFlopGate is        
signal out1,out2,out3,out4 : std_logic;

begin
    out1    <= NOT(J AND Clk AND out4);                 
    out2    <= NOT(K AND Clk AND out3); 
    out3    <= out1 NAND out4;
    out4    <= out2 NAND out3; 
    Q       <= out3;
    Qbar    <= out4;    
end result;

Respuestas (3)

Si desea usar el reloj, debe escribir el proceso, que es sensible en algún borde del reloj. EDITAR: también necesita inicializar out3 y out4:

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

entity JKFlipFlopGate is
    port(
        J,K,Clk : in std_logic;        --JK Flip-Flop gate input 1 & 2
        Q,Qbar : out std_logic         --JK Flip-Flop gate output
    );
end JKFlipFlopGate;

architecture result of JKFlipFlopGate is        
signal out1,out2: std_logic;
signal out3 : std_logic := '0'; -- Need proper initialization
signal out4 : std_logic := '0'; -- Need proper initialization

begin

    process(clk)

    begin
       if(rising_edge(clk)) then
           out1    <= NOT(J AND out4);
           out2    <= NOT(K AND out3); 
       end if;
    end process

    out3    <= out1 NAND out4;
    out4    <= out2 NAND out3; 
    Q       <= out3;
    Qbar    <= out4;    

end result;

De esta manera debería funcionar (pero no lo probé).

Ahora entiendo el problema, gracias por tu ayuda. Probaré tu código.
Puede haber un problema: es posible que deba inicializar algunas de las señales de salida. Pruebe esto y publíquelo aquí, y editaré mi respuesta si es necesario. Y por favor, acepta la respuesta si te es útil :)
Ok, ahora lo sé (de otra respuesta, gracias Claudio Avi Chami) - tienes que inicializar out3 y out4
El problema con esta respuesta es que no implementa el diagrama de circuito en la pregunta. El diagrama muestra 4 puertas NAND, pero este código implementa 4 puertas NAND y dos registros.
@scary_jeff Pero sin usar los registros, mostrará salidas desconocidas
Bueno, este diseño tiene reloj. 4 puertas NAND desnudas no tendrían reloj. Entonces, en realidad, este circuito tiene dos registros ocultos en las dos primeras puertas. ¿no es así? ¿O tal vez no entendí bien la pregunta?
En realidad, la pregunta es diseñar solo un flip-flop JK, cualquier método utilizado es aceptable
Esto implementa dos DFF y algo de lógica combinatoria, no el circuito requerido. Además, nunca use valores iniciales en señales en lógica sintetizable, use un reinicio adecuado. Esa es la diferencia entre 'escribir VHDL' y el diseño de circuitos lógicos. Votación negativa.
Muchas veces usé valores iniciales en señales en código sintetizado y nunca tuve problemas con eso. ¿Por qué no debería hacer eso? Además, no hay reinicio en este circuito, por lo que si somos estrictos con el diseño, fallaría en caso de usar el reinicio. Y, como ya señalé, este diseño NO son 4 puertas NAND desnudas. @Elzy ya estuvo de acuerdo (si entendí correctamente), que en realidad este circuito no es específicamente lo que quería lograr. En la descripción agregó algunas informaciones adicionales.
Te saliste con la tuya en FPGA basado en RAM. Los valores iniciales son un hábito terrible. Un reinicio es fácil de hacer, muy común (mire esas toneladas de IP profesional) y mantiene su IP portátil, es decir, puede ponerlo en un CPLD o en un FPGA diferente (no basado en RAM) y funcionará. Otros pueden reutilizar sus diseños en otros dispositivos o puede irse y trabajar en otro lugar en diferentes FPGA: aprenda a mantener los diseños portátiles. Puede hacer un reinicio interno para su FPGA basado en RAM usando el valor inicial allí solo (DFF SR4 configurado en 1, CLKd en 0). Vaya a flashear FPGA, los valores iniciales no funcionarán. Diseñe buenos circuitos, no buenas simulaciones.
Ok, ¿qué tal esta pregunta y la primera respuesta? Dice claramente, que en algunos casos es mejor no tener reinicio, sino valores iniciales. En nuestra pregunta, no se trataba de tecnología, por lo que no hay motivo para rechazar si elijo una opción de las dos disponibles y respondo correctamente a la pregunta. De todos modos, gracias por provocarme a investigar un poco y enseñarme algo nuevo :)
En lugar de 'claramente dice', es 'algunas personas piensan' que es mejor. Afortunadamente, sabemos que no lo es. Esas personas están equivocadas: solo ven su pequeña parte del mundo técnico y no saben cómo viajar :-) Sus cosas no funcionarían en Microsemi, Lattice FPGA: circuitos rotos a través de un diseño perezoso. Algunos simplemente escriben VHDL. Miro todo el circuito, evalúo lo que necesita, diseño el circuito y finalmente lo escribo en VHDL. Como dije al principio, ves esos 10k de núcleos de IP, todos con reinicio, cero punto cero con valores iniciales. Downvote fue por respaldar valores iniciales horribles.
Proporcionó un argumento sólido sobre la lógica que no tiene reinicio incorporado, por lo que no es "algunas personas piensan". Creo que eres un poco demasiado arrogante al decir esto. Pero es su negocio. De todos modos, gracias por la conversación y los consejos para el futuro.
En el diseño de ultra alta velocidad, a menudo no usa reinicios, ya que los reinicios requieren recursos de enrutamiento que debe minimizar. Necesita esos recursos para lograr un retraso de tiempo mínimo. Por lo tanto, los ingenieros de aplicaciones de Xilinx aconsejan minimizar el uso de reinicio a "solo donde realmente se requiere".

Para empezar, la forma convencional de diseñar un flip flop JK en VHDL se vería así:

signal Q_s : std_logic;

process(clk)
begin
  if (rising_edge(clk)) then
    if (J = '1' and K = '1') then
      Q_s <= not Q_s;
    elsif(J = '1') then
      Q_s <= '1';
    elsif(K = '1') then
      Q_s <= '0';
    end if;
  end if;
end process;

Q <= Q_s;
Qbar <= not Q_s;

Aquí estamos haciendo inferir un registro, con un comportamiento equivalente a un flip flop JK. Tenga en cuenta que con este código, si afirma Jy Kjuntos, antes de afirmar individualmente, entonces en la simulación, la salida Qserá indefinida. Si el estado inicial no importa por alguna razón, puede inicializarlo en la definición usando signal Q_s : std_logic := '0';( '1'sería igualmente válido). Si el estado inicial importa, debe agregar una cláusula de reinicio específica para establecer este estado.

Volviendo a su pregunta real, en realidad no hay nada malo con su código; sintetizará correctamente y funcionará 'correctamente', pero no dará los resultados esperados bajo simulación. Puede implementar su flip flop JK de una manera que simule correctamente y se pueda sintetizar con el resultado que coincida exactamente con su diagrama de circuito. Para empezar, aquí hay un banco de pruebas simple:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity tb is
end tb;

architecture a of tb is
  constant CLK_PERIOD : time      := 100 ns;
  signal J            : std_logic := '0';
  signal K            : std_logic := '0';
  signal Clk          : std_logic := '0';
  signal Q            : std_logic;
  signal Qbar         : std_logic;
begin
  Clk <= not Clk after CLK_PERIOD / 2;

  uut : entity work.JKFlipFlopGate
    port map(
      J    => J,
      K    => K,
      Clk  => Clk,
      Q    => Q,
      Qbar => Qbar
    );

  process
  begin
    wait for CLK_PERIOD;
    J <= '1';
    wait for CLK_PERIOD;
    J <= '0';
    wait for CLK_PERIOD;
    k <= '1';
    wait for CLK_PERIOD;
    K <= '0';
    wait for CLK_PERIOD;
    J <= '1';
    k <= '1';
    wait for CLK_PERIOD;
    J <= '0';
    k <= '0';
    wait;
  end process;

end a;

El problema de simular su código tal como está es que, dado que todas sus outxseñales no están inicializadas, todo el circuito está en un estado desconocido, con varias señales que tienen el valor 'U'en el punto en que comienza la simulación. Al agregar la inicialización, sus outxdefiniciones de señal se ven así:

signal out1 : std_logic := '1';
signal out2 : std_logic := '1';
signal out3 : std_logic := '0';
signal out4 : std_logic := '1';

Tenga en cuenta que estos valores de inicialización solo afectan a la simulación; dado que su código no describe ningún elemento síncrono (más bien, una función combinatoria con un comportamiento similar a un elemento síncrono), no tienen impacto en el diseño sintetizado, ya sea que su cadena de herramientas los admita o no.

La simulación de este diseño actualizado funciona correctamente cuando o Jse Kestablece en '1'. Sin embargo, cuando ambos se establecen altos, hay una condición de carrera y el circuito oscila. Esto coincide con el comportamiento del circuito real cuando el pulso del reloj permanece alto después de que la salida Qha cambiado. Como no hay retrasos en la descripción, esta oscilación se producirá en ciclos delta de simulación, y se alcanzará el límite de iteraciones de la simulación. Podemos verlos más fácilmente incorporando un retardo de puerta rudimentario en el diseño:

entity JKFlipFlopGate is
    generic(
        GATE_DELAY : time := 0 ns   -- Default needed for synth
    );
    port(
        J, K, Clk : in  std_logic;
        Q, Qbar   : out std_logic
    );
end JKFlipFlopGate;

architecture result of JKFlipFlopGate is
    signal out1 : std_logic := '0';
    signal out2 : std_logic := '0';
    signal out3 : std_logic := '1';
    signal out4 : std_logic := '0';

begin
    out1 <= not(J AND Clk AND out4) after GATE_DELAY;
    out2 <= not(K AND Clk AND out3) after GATE_DELAY;
    out3 <= out1 nand out4 after GATE_DELAY;
    out4 <= out2 nand out3 after GATE_DELAY;
    Q    <= out3;
    Qbar <= out4;
end result;

Normalmente, diría que no debe usar aftercláusulas en el código que se implementará en hardware real, pero esto es solo un ejercicio, y la síntesis ignora estas cláusulas. La simulación agrega lo siguiente:

constant GATE_DELAY : time := 1 ns;

Y la uutinstanciación se convierte en:

  uut : entity work.JKFlipFlopGate
    generic map(
      GATE_DELAY => GATE_DELAY
    )
    port map(
      J    => J,
      K    => K,
      Clk  => Clk,
      Q    => Q,
      Qbar => Qbar
    );

Ahora puede ver las oscilaciones que resultan de un pulso de reloj demasiado largo en la forma de onda de simulación:

Oscilaciones JK

Podemos acortar fácilmente el ancho del pulso del reloj para deshacernos de estas oscilaciones agregando una nueva señal de 'pulso', derivada del reloj:

signal Clk_pulse    : std_logic := '0';

Tenga en cuenta que este es un código solo de simulación, por lo que puede usar los valores iniciales como desee. A continuación, un proceso para derivar el pulso del reloj:

  process(Clk)
  begin
    if (rising_edge(Clk)) then
      Clk_pulse <= '1', '0' after 2 * GATE_DELAY;
    end if;
  end process;

Y, por último, en la uutcreación de instancias, Clk => Clk,se convierte en Clk => Clk_pulse,.

Con todos estos cambios realizados, el diseño simula con el comportamiento correcto y todavía se transforma correctamente en el circuito de diseño original:

reloj 'pulso'

circuito elaborado

Buena respuesta. Pero no predeterminaría el retraso de la puerta a 0 ns, o tendrá el mismo problema nuevamente.
@JHBonarius El valor predeterminado es, como dice, para síntesis. Podría agregar una especificación para ese genérico en su herramienta, pero eso sería específico de la herramienta.

Una simulación VHDL como esta nunca funcionará sin una señal de reinicio. Al comienzo de la simulación, se desconoce el valor de Q y Qbar y, dado que se retroalimentan a la entrada, la condición desconocida se propaga a través de todas las puertas.

Sí puede. Solo necesita definir valores iniciales.