Otros

¿Cómo utilizar múltiples relojes en VHDL?

Figura 2: Resultado de la simulación al utilizar el reloj 1.

Digamos que, por alguna razón, necesitas incluir dos relojes en tu módulo en VHDL.

¿Cómo le haces?

Puedes ver el siguiente video o continuar leyendo la entrada:

En el listado 1 tenemos un fragmento de código con lo que queremos hacer: modificar una salida en base a dos frecuencias distintas, el reloj 1 o el reloj 2.

salida_p: process (clk_1, clk_2, reset) begin
    if reset = '1' then
        salida_tmp <= '0';
    elsif rising_edge(clk_1) then
        salida_tmp <= NOT salida_tmp;
    elsif rising_edge(clk_2) then
        salida_tmp <= NOT salida_tmp;
    end if;
end process;

Al querer sintetizar en Vivado, obtenemos un error:

Figura 1: Error de síntesis en Vivado.

Figura 1: Error de síntesis en Vivado.

Resulta que no puede haber un else, o elsif, después de la cláusula donde revisamos un flanco de subida.

¡Bah!

Entonces, si queremos hacer lo mismo pero en dos frecuencias distintas, debemos crear un nuevo proceso para seleccionar el reloj.

Esto quiere decir que necesitaremos una entrada adicional para seleccionar el reloj y una señal interna para almacenar la frecuencia deseada.

Y en el nuevo proceso utilizamos nuestra entrada de selección, sel, para mandar uno de los dos relojes a la señal interna.

Finalmente, se modifica el proceso original, eliminando el elsif del segundo reloj y tomando como reloj a nuestra señal interna.

El resultado de estos cambios, que producen código que sí sintetiza, se muestra en el listado 2:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity dos_relojes is
    Port (
        clk_1 : in  STD_LOGIC; -- Primer frecuencia
        clk_2 : in  STD_LOGIC; -- Segunda frecuencia
        sel   : in  STD_LOGIC; -- Selector de frecuencia
        reset : in  STD_LOGIC; -- Reset de señal de salida
        salida: out STD_LOGIC  -- Salida para demostración
    );
end dos_relojes;

architecture Behavioral of dos_relojes is
    signal salida_tmp: STD_LOGIC := '0';
    signal clk_tmp: STD_LOGIC := '0';
begin
    clk_select_p: process (clk_1, clk_2, sel) begin
        if sel = '0' then
            clk_tmp <= clk_1;
        else
            clk_tmp <= clk_2;
        end if;
    end process;

    salida_p: process (clk_tmp, reset) begin
        if reset = '1' then
            salida_tmp <= '0';
        elsif rising_edge(clk_tmp) then
            salida_tmp <= NOT salida_tmp;
        end if;
    end process;

    salida <= salida_tmp;
end Behavioral;

Realizamos un banco de pruebas, mostrado completamente en el listado 3:

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;

entity dos_relojes_tb is
end;

architecture bench of dos_relojes_tb is
    component dos_relojes
        Port (
            clk_1 : in  STD_LOGIC;
            clk_2 : in  STD_LOGIC;
            sel   : in  STD_LOGIC;
            reset : in  STD_LOGIC;
            salida: out STD_LOGIC
        );
    end component;

    signal clk_1: STD_LOGIC;
    signal clk_2: STD_LOGIC;
    signal sel: STD_LOGIC;
    signal reset: STD_LOGIC;
    signal salida: STD_LOGIC ;

    constant clock_1_period: time := 10 ns;
    constant clock_2_period: time := 50 ns;
    signal stop_the_clock: boolean;
begin
    uut: dos_relojes port map (
        clk_1  => clk_1,
        clk_2  => clk_2,
        sel    => sel,
        reset  => reset,
        salida => salida
    );

    stimulus: process
    begin
        -- Inicialización
        reset <= '1';
        wait for 5 ns;
        reset <= '0';
        wait for 5 ns;

        -- Los primeros 1500 ns, tras reset, será clk_1
        sel <= '0';
        wait for 1500 ns;
        -- Los siguientes 1500 ns, será clk_2
        sel <= '1';
        wait for 1500 ns;

        stop_the_clock <= true;
        wait;
    end process;

    clocking_1: process begin
        while not stop_the_clock loop
            clk_1 <= '0', '1' after clock_1_period / 2;
            wait for clock_1_period;
        end loop;
        wait;
    end process;

    clocking_2: process begin
        while not stop_the_clock loop
            clk_2 <= '0', '1' after clock_2_period / 2;
            wait for clock_2_period;
        end loop;
        wait;
    end process;
end;

Seleccionamos inicialmente el primer reloj durante 1,500 nanosegundos (líneas 46 y 47), lo que da una señal de salida de 20 ns (nuestra constante de clock_1_period es de 10 ns).

El resultado de esta primera parte se muestra en la figura 2:

Figura 2: Resultado de la simulación al utilizar el reloj 1.

Figura 2: Resultado de la simulación al utilizar el reloj 1.

La segunda mitad de la simulación utiliza el segundo reloj (líneas 49 y 50), por lo que la señal de salida es de 100 nanos (clock_2_period es de 50 ns). Ésto se refleja en la figura 3:

Figura 3: Resultado de la simulación al utilizar el reloj 2.

Figura 3: Resultado de la simulación al utilizar el reloj 2.

Muy bien. Ahora sabemos que no podemos tener dos relojes en un mismo proceso.

Pero nada nos impide hacer un proceso que asigne uno de los dos relojes a otra señal que funja como reloj único.

Ah, cierto, el enlace de descarga:

Multireloj.rar

Si te quedan dudas, no dudes en comentar, ya sea aquí o en el video.

You Might Also Like

1 Comentario

  • Responder
    Jaime
    febrero 5, 2019 at 3:48 am

    Hola,
    Te falta el multiplexor de reloj. Como metas eso en un diseño de verdad va a reventar Vivado por 7 sitios 🙂

  • Deja tu comentario