Generación de relojes no superpuestos en FPGA usando VHDL

Estoy tratando de implementar circuitos de capacitores conmutados y, por lo tanto, necesito generar un reloj de dos fases que no se superponga. He estado tratando de usar un FPGA para lo mismo. Desafortunadamente, mi herramienta de síntesis, Quartus II, lanza advertencias de tiempo. Además, cuando descargo el código en la FPGA (FPGA de la serie Altera MAX7000S), observo claramente niveles metaestables y resultados impredecibles.

El código que he escrito para implementar esto se da a continuación:

la arquitectura clock_gen_integrator_arch de clock_gen_integrator es la señal counter_15 : STD_LOGIC_VECTOR(3 hasta 0); señal phi1_sig, phi2_sig: STD_LOGIC; contador de señal: STD_LOGIC_VECTOR (14 hasta 0);

begin
phi1     <= phi1_sig;
phi2     <= phi2_sig;

signal_gen: process (reset, clock25M) begin
    if(reset = '0') then
        counter_15    <= (others => '0');
        counter       <= (others => '0');
        phi1_sig      <= '1';
        phi2_sig      <= '0';
        reset_int     <= '1';
    elsif(clock25M = '1' and clock25M'EVENT) then
              if(counter < "000010000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "011100000000000" and counter_15 < "1111") then
              phi1_sig      <= '1';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "100010000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "111100000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '1';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "111111111111111" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter = "111111111111111" and counter_15 < "1111") then
              counter_15    <= counter_15 + 1;
              counter       <= counter + 1;
              reset_int     <= '0';
          else 
              phi1_sig      <= phi1_sig;
              phi2_sig      <= phi2_sig;
              reset_int     <= '0';
          end if;

        end if;
    end process;
end architecture;

Desafortunadamente, no puedo incluir el código completo aquí, tengo problemas de formato. Sin embargo, dado que es solo la declaración de la entidad, la he dejado de lado. phi1(out), phi2(out), reset(in), reset_int(out), clock25M(in) están presentes en la lista de puertos.

En este código, elijo arbitrariamente la frecuencia y los ciclos de trabajo de los relojes requeridos. Específicamente quiero 15 pulsos de phi1 y 15 pulsos de phi2 y counter_15 me ayuda a lograr esto.

QuartusII me dice que tengo infracciones de tiempo de configuración.Infracciones de tiempo de configuración reportadas por QuartusII.  Hay 100 de ellos en total.

esta es la salida

Perdón por la imagen en escala de grises, tuve que reducir el tamaño de la imagen de alguna manera para poder subirla. El primer canal es la salida phi1 y el segundo canal es la salida phi2. Siendo nuevo en la herramienta y en el análisis de tiempo, estaría agradecido si alguien pudiera señalar lo que estoy haciendo mal y cómo puedo solucionar la violación de tiempo. Además, cualquier consejo sobre cómo evitar estos problemas en general es bienvenido.

¿Ha considerado cambiar el código para deshacerse de las comparaciones "<"? Estos son lógicamente costosos. Podría ser que su lógica sea demasiado lenta para cumplir con el tiempo. Dado que su contador siempre se incrementa, puede seleccionar puntos específicos en el conteo para que cambien las salidas de PHI y hacer una comparación de igualdad para esos puntos. Las comparaciones de igualdad son mucho más rápidas, ya que no requieren un sumador y el retardo de propagación de acarreo asociado.
No creo que haya simulado esto porque después de un reinicio, el contador siempre se incrementa. A VHDL no le gusta eso.
@ crj11, gracias por tu comentario. Anteriormente tenía un código que usaba igualdades en lugar del operador <. Tenía las mismas advertencias de sincronización y problemas similares con la salida; el 0 lógico y el 1 lógico no eran un solo voltaje constante. Durante la fase 1 lógico oscilaba con un valor medio probablemente igual a 3,3V y durante la fase 0 lógico con un valor medio probablemente igual a 0V.
@Oldfart, gracias por tu comentario. Yo también lo simulé. Tanto la presíntesis como las simulaciones posteriores a la síntesis fueron perfectas y tal como esperaba que fueran. Sin embargo, con respecto a su observación sobre cómo estoy incrementando mi contador, ¿quiere decir que debería haber dado contador <= "00...0" cuando se convierte en "11...1"? Lo intenté hace un momento y no hace ninguna diferencia ni en los resultados de la síntesis ni en los de la simulación.
No estoy familiarizado con Quartus, así que estoy luchando con el tiempo. -11 de holgura y 9 de retraso lo interpretaría como un período de 20 (ns?) Que sería un reloj de 50MHz. Pero eso no coincide con -7 de holgura y 5 de retraso. La única consistencia es el delta entre holgura y retraso, que es 2 (¿nS?) En todas partes, pero eso sería 500MHz. reloj. A 25 MHz, esperaría que su circuito cumpla con el tiempo. No es tan complejo.
¿Qué se muestra exactamente en el seguimiento del alcance? ¿El pin FPGA está impulsando algo más que la sonda de alcance? Que tipo de sonda es? ¿Para qué tipo de salida y corriente de salida tiene las salidas configuradas?
@ crj11, la traza del osciloscopio muestra la forma de onda de voltaje de las dos fases de reloj que se supone que no se superponen: phi1 y phi2. El pin FPGA se conectó a los terminales de puerta de un interruptor MOSFET (de CD4066). Sin embargo, lo probé aislando las puertas MOSFET y los resultados no fueron muy diferentes. Sobre el tipo de sonda, no estoy seguro de los detalles, pero lo más probable es que sea esto: keysight.com/en/pd-1938439-pn-N2862B/… Y sobre la corriente O/P, no la he medido y el O/P es una forma de onda V (si eso es lo que estás preguntando, perdóname).
@Amogh, generalmente en el archivo de configuración de FPGA puede especificar el tipo de lógica para la salida, por ejemplo, LVCMOS, y la corriente de salida máxima, por ejemplo, 16ma. Si no lo especifica, utiliza algún valor predeterminado. Debería poder ver cuáles son los tipos de salida y las corrientes en uno de los archivos de informe generados por Quartus.
Correcto, he estado usando el tipo de salida TTL predeterminado y la corriente de salida máxima según la hoja de datos es de 25 mA por pin.
Idealmente, no genera señales de reloj en código HDL, sino con los bloques PLL/DLL dedicados, que a menudo tienen varias opciones de fase de salida. Si necesita generar relojes derivados de HDL, debe hacerlo a través de un registro de salida con un reloj común más rápido y otorgar a la herramienta restricciones de tiempo de reloj a salida para que pueda lograr una uniformidad suficiente en sus retrasos.

Respuestas (4)

Un método general para mejorar la sincronización es dividir la lógica en varios ciclos mediante el registro de resultados.

Podrías intentar algo como esto...

signal_gen: process (reset, clock25M) begin
    if(reset = '0') then
        counter_15    <= (others => '0');
        counter       <= (others => '0');
        phi1_sig      <= '1';
        phi2_sig      <= '0';
        reset_int     <= '1';
        counter_15_not_done <= true;
        count_lt_000010000000000 <= false;
        count_lt_011100000000000 <= false;
        count_lt_100010000000000 <= false;
        count_lt_111100000000000 <= false;
        count_lt_111111111111111 <= false;
        count_eq_111111111111111 <= false;
    elsif(clock25M = '1' and clock25M'EVENT) then
          --the comparisons are computed in parallel and registered
          counter_15_not_done <= counter_15 < "1111";
          count_lt_000010000000000 <= count < "000010000000000";
          count_lt_011100000000000 <= count < "011100000000000";
          count_lt_100010000000000 <= count < "100010000000000";
          count_lt_111100000000000 <= count < "111100000000000";
          count_lt_111111111111111 <= count < "111111111111111";
          count_eq_111111111111111 <= count = "111111111111111";
          --the logic now only depends on the registered results.
          --The registered results are only 7 bits rather than 19 bits
          --this should have much better timing
          if(count_lt_000010000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_011100000000000 and counter_15_not_done) then
              phi1_sig      <= '1';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_100010000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_111100000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '1';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_111111111111111 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_eq_111111111111111 and counter_15_not_done) then
              counter_15    <= counter_15 + 1;
              counter       <= counter + 1;
              reset_int     <= '0';
          else 
              phi1_sig      <= phi1_sig;
              phi2_sig      <= phi2_sig;
              reset_int     <= '0';
          end if;

        end if;
    end process;
end architecture;

Tenga en cuenta que los resultados registrados retrasarán los recuentos reales en 1 ciclo de reloj, por lo que es posible que deba ajustar los puntos de comparación 1 ciclo atrás.

Había intentado algo en este sentido; Puse un vector temporal que se asignaría como: contador_nxt <= contador + 1 en el borde positivo. Y luego, en el borde negativo (en otro proceso) asigné counter <= counter_nxt. Sin embargo, eso apenas hizo una diferencia. Acabo de probar tu código también en el hardware. Si bien simula perfectamente en mi computadora portátil, el analizador de tiempo aún muestra violaciones de tiempo y no produce los resultados esperados en el hardware. Ahora estoy empezando a dudar si todo está bien con el FPGA. Gracias por tu respuesta, aprendí algo nuevo!

La igualdad se implementa con menos niveles de retraso de 'puerta' que la magnitud ("<"), lo que implica una resta y no se optimizará por completo si hay valores de operando mayores que el operando de magnitud estática más grande. (El operando de comparación del contador más alto es x"7800", el conteo más alto del contador es x"7FFF".)

Use comparaciones de igualdad solamente, por ejemplo:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;          -- ADDED

entity clock_gen_integrator is
    port (
        clock25M:   in  std_logic;
        reset:      in  std_logic;
        reset_int:  out std_logic;
        phi1:       out std_logic;
        phi2:       out std_logic
    );
end entity;

architecture foo of clock_gen_integrator is 

    signal counter_15:  unsigned (3 downto 0); -- WAS std_logic_vector
    signal phi1_sig:    std_logic;
    signal phi2_sig:    std_logic; 
    signal counter:     unsigned(14 downto 0); -- WAS std_logic_vector 
    signal notcount15:  boolean;
    signal notcount15h: boolean; 
begin

    phi1 <= phi1_sig;
    phi2 <= phi2_sig;

    notcount15 <= counter_15 /= 15;  -- 4 input NAND gate

signal_gen: 
    process (reset, clock25M) 
    begin
        if reset = '0' then
            counter_15    <= (others => '0');
            notcount15h     <= true;
            counter       <= (others => '0');
            phi1_sig      <= '1';
            phi2_sig      <= '0';
            reset_int     <= '1';
        -- elsif clock25M = '1' and clock25M'EVENT then 
        elsif rising_edge (clock25M) then
            -- the counters:
            counter <= counter + 1;
            if counter = x"3800" and notcount15 then
                counter_15 <= counter_15 + 1;
            end if;
            -- notcount15h holdover flip flop for phi2
            if counter = x"3800" and not notcount15 then
                notcount15h <= false;
            end if;
            -- reset_int flip flop:
            reset_int <= not reset;
            -- toggle phi1:
            if counter = x"400"  and notcount15 then
                phi1_sig <= '1';
            elsif counter = x"3800" and notcount15 then
                phi1_sig <= '0';
            end if;
            -- toggle phi2:
            if counter = x"4400" and notcount15h then
                phi2_sig <=  '1';
            elsif counter = x"7800" and notcount15h then
                phi2_sig <= '0';
            end if;  
            -- four equality comparators to specific 15 bits of counter
        end if;
    end process;
end architecture;


library ieee;
use ieee.std_logic_1164.all;

entity clock_gen_integrator_tb is
end entity;

architecture foo of clock_gen_integrator_tb is
    signal clock25M:   std_logic := '0';
    signal reset:      std_logic := '1';
    signal reset_int:  std_logic;
    signal phi1:       std_logic;
    signal phi2:       std_logic;
begin

CLOCK:
    process
    begin
        wait for 20 ns;
        clock25M <= not clock25m;
        if now > 21 ms then
            wait;
        end if;
    end process;
DUT:
    entity work.clock_gen_integrator
        port map (
            clock25M => clock25M,
            reset => reset,
            reset_int => reset_int,
            phi1 => phi1,
            phi2 => phi2
        );

STIMULUS:
    process
    begin
        wait for 30 ns;
        reset <= '0';
        wait for 80 ns;
        reset <= '1';
        wait;
    end process;

end architecture;

El banco de pruebas demuestra 15 relojes phi1 y phi2 ("Quiero específicamente 15 pulsos de phi1 y 15 pulsos de phi2 y counter_15 me ayuda a lograr esto") en los mismos valores de contador que en la arquitectura original clock_gen_integrator_arch:

clock_gen_integrator_tb.jpg

Probé su código sugerido en el hardware y los niveles de voltaje no eran constantes. También observo algunos pequeños pulsos después de los quince ciclos de reloj. Ahora estoy dudando de mi hardware. Además, gracias por los cambios recomendados; parece que todavía tengo mucho que aprender.

Todos los bits inferiores en sus puntos de transición son cero (sin contar la instancia en la que borra phi2dos veces, por lo que puede dividir el reloj varias veces y usar un contador más pequeño).

También puede dividir la generación de un tren continuo de pulsos de la longitud deseada (bits de orden inferior del contador) y el enrutamiento a una salida (bits de orden superior), por ejemplo, 16 pulsos con un 1 8 ciclo de trabajo y se pueden lograr dos salidas con un contador de 8 bits (ni siquiera se ha probado la compilación, pero debería hacerse una idea):

pulse <= '1' WHEN counter(2 downto 0) = "000" ELSE '0';
output <= counter(3);
phi1 <= pulse AND NOT output;
phi2 <= pulse AND output;

Han countercorrido hacia arriba y se detienen después "11111111".

Dependiendo de qué más esté sucediendo en el diseño, probablemente también usaría un PLL para derivar un reloj lento (1 MHz) y dividirlo desde allí.

Desafortunadamente, esta es una implementación deficiente de un diseño que no tiene especificaciones ni tolerancias. Siempre comience con las especificaciones y tolerancias del diseño antes de intentar diseñar una solución.

Su falta de diseño de impedancia (RdsOn o interruptor ESR) y la caída de la fuga muestran evidencia de ruido de conmutación y timbre cuantificado.

La elección de las tapas no debe ser de cerámica que tenga histéresis y problemas microfónicos en los diseños S&H, a menos que sea del tipo NP0.

Dado;

circuitos de capacitores conmutados y, por lo tanto, necesito generar un reloj de dos fases que no se superponga

¿Cuáles son los siguientes? Incluya las tolerancias esperadas para cada uno.

  • respuestas deseadas del filtro y tolerancia
  • reloj de entrada f
  • reloj de salida f
  • límite de corriente del reloj de salida
  • capacitancia de carga de salida (por lo tanto, tiempo de subida)
  • el retraso de salida se desvía del peor de los casos de latencia
  • cambiar la resistencia
  • capacitancia conmutada, por lo tanto, tiempo de subida
  • tiempo muerto no superpuesto de relojes bifásicos
  • efecto sobre la constante de tiempo RC con tiempo muerto (RdsOn/d)
  • temporización de ancho de pulso requerida para flancos de ataque y de salida en V+/2 o “PW50”
  • sensibilidad a lo anterior con la temperatura y la tensión de alimentación.

Cuantas más especificaciones defina por adelantado que puedan causar errores, más posibilidades tendrá de hacerlo bien la primera vez . De lo contrario, iteraciones en las especificaciones de diseño y reintento o confusión cuando falla.

Ahora, ¿qué método será más fácil para crear estos requisitos? ¿Analógico? Como lo hacen todos los diseños de puentes completos o digitales con límites de resolución de tiempo de cuantificación o ?

¿Qué circuitos ha leído que están publicados en los libros de la aplicación CMOS que ya funcionan? Y si no, ¿Pórque no?

pd
Hay muchas herramientas para cambiar el tamaño de la imagen o la relación de compresión para reducir el tamaño del archivo y conservar el color de 32 bits.

Aumentar la distancia y la mano firme con la cámara, luego el enfoque automático, directamente al monitor dará una imagen más clara, luego recortar según sea necesario.