14 votos

Cómo dividir 50MHz hasta 2Hz en VHDL en FPGA de Xilinx

Tengo una placa FPGA de Xilinx, con un cristal de 50MHz. Necesito dividir eso a 2Hz en VHDL. ¿Cómo hago esto?

13 votos

Entonces, ¿qué has intentado realmente?

0 votos

¿Por qué no usar Xilinx Clock Manager IP?

20voto

Jonas Puntos 1764

Básicamente, hay dos formas de hacer esto. La primera es usar el núcleo sintetizador de reloj nativo de Xilinx. Una de las ventajas de esto es que las herramientas de Xlinx reconocerán el reloj como tal y lo rutearán a través de las vías requeridas. Las herramientas también manejarán cualquier restricción de tiempo (no realmente aplicable en este caso, ya que es un reloj de 2Hz)

La segunda forma es usar un contador para contar el número de pulsos de reloj más rápidos hasta que haya pasado la mitad de su período de reloj más lento. Por ejemplo, para tu caso, el número de pulsos de reloj rápidos que componen un período de reloj de un ciclo de reloj lento es 50000000/2 = 25000000. Dado que queremos la mitad de un período de reloj, eso es 25000000/2 = 12500000 para cada mitad del ciclo. (la duración de cada alto o bajo).

Así es como se ve en VHDL:

library IEEE;
use IEEE.STD_LOGIC_1164.all;

-- Descomenta la siguiente declaración de la librería si estás usando
-- funciones aritméticas con valores firmados o no firmados
use IEEE.NUMERIC_STD.all;

entity scale_clock is
  port (
    clk_50Mhz : in  std_logic;
    rst       : in  std_logic;
    clk_2Hz   : out std_logic);
end scale_clock;

architecture Behavioral of scale_clock is

  signal prescaler : unsigned(23 downto 0);
  signal clk_2Hz_i : std_logic;
begin

  gen_clk : process (clk_50Mhz, rst)
  begin  -- proceso gen_clk
    if rst = '1' then
      clk_2Hz_i   <= '0';
      prescaler   <= (others => '0');
    elsif rising_edge(clk_50Mhz) then   -- flanco de reloj ascendente
      if prescaler = X"BEBC20" then     -- 12 500 000 en hex
        prescaler   <= (others => '0');
        clk_2Hz_i   <= not clk_2Hz_i;
      else
        prescaler <= prescaler + "1";
      end if;
    end if;
  end process gen_clk;

clk_2Hz <= clk_2Hz_i;

end Behavioral;

Detalles a tener en cuenta:

  • El reloj generado es cero durante el reset. Esto está bien para algunas aplicaciones y no para otras, solo depende de para qué necesitas el reloj.
  • El reloj generado será rutado como una señal normal por las herramientas de síntesis de Xilinx.
  • 2Hz es muy lento. Simular durante un segundo va a tomar un tiempo. Es una pequeña cantidad de código, así que debería ser relativamente rápido de simular incluso por 1 segundo, pero si comienzas a agregar código, el tiempo tomado para simular un ciclo de reloj de 2 Hz podría ser significativamente largo.

EDITAR: clk_2Hz_i se utiliza para almacenar en búfer la señal de salida. VHDL no permite usar una señal a la derecha de una asignación cuando también es una salida.

1 votos

No está mal, pero puedes agregar/comparar un unsigned con un entero, así que: if prescaler = 50_000_000/4 then ... y prescaler <= prescaler + 1; serían un poco más simples.

0 votos

@StaceyAnne Al intentar esto, obtengo "No se puede leer del objeto 'out' clk_o; use 'buffer' o 'inout'". ¿Me he perdido algo?

0 votos

@evading, se necesita un buffer en la salida. VHDL no le gusta el hecho de que clk_2Hz sea una salida, pero aún así su valor se está leyendo en esta línea clk_2Hz <= not clk_2Hz;. He editado la solución.

9voto

user11915 Puntos 16

Utilice un preescalador de reloj.

Su valor de preescalador será (frecuencia_reloj/frecuencia_deseada)/2, por lo que (50Mhz(50,000,000)/2hz(2))/2 = 12,500,000 que en binario sería 101111101011110000100000.

Más simple: (50,000,000)/2)/2 = 12,500,000 convertir a binario -> 101111101011110000100000

Aquí hay un poco de código sobre qué hacer: Usa newClock para cualquier cosa que necesites 2hz para...

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ClockPrescaler is
    port(
        clock   : in STD_LOGIC; -- 50 Mhz
        Led     : out STD_LOGIC
    );
end ClockPrescaler;

architecture Behavioral of ClockPrescaler is
    -- el preescalador debe ser (frecuencia_reloj/frecuencia_deseada)/2 porque quieres un flanco de subida en cada periodo
    signal prescaler: STD_LOGIC_VECTOR(23 downto 0) := "101111101011110000100000"; -- 12,500,000 en binario
    signal prescaler_counter: STD_LOGIC_VECTOR(23 downto 0) := (others => '0');
    signal newClock : std_logic := '0';
begin

    Led <= newClock;

    countClock: process(clock, newClock)
    begin
        if rising_edge(clock) then
            prescaler_counter <= prescaler_counter + 1;
            if(prescaler_counter > prescaler) then
                -- Iterar
                newClock <= not newClock;

                prescaler_counter <= (others => '0');
            end if;
        end if;
    end process;

end Behavioral;

0 votos

Parece que estás generando dos relojes, uno de 0.5 Hz y otro de 1 Hz? (ya que tu periodo de reloj es tu preescalador * 2?). Además, el "+" dará un error, ya que estás sumando slvs, y no estoy seguro de usar la propiedad de overflow del sumador de esta manera en cualquier caso. ¿por qué no simplemente hacer newClock: std_logic := '0', contar hasta preescalador/2 y asignar newClk <= not newClk?

0 votos

Gracias, mi lógica estaba un poco equivocada. ¡He actualizado mi publicación inicial con algo de código probado ahora y algunas de tus sugerencias!

0 votos

¡Ugh, todos esos unos y ceros y un comentario para decir lo que realmente es! ¿Por qué no usar el compilador para que lo haga por ti??? Y de todas formas, ¿por qué no usar enteros?

8voto

Martin Thompson Puntos 6509

Por lo general, no quieres realmente marcar algo tan lento, simplemente crea un habilitador a la velocidad correcta y úsalo en la lógica:

 if rising_edge(50MHz_clk) and enable = '1' then

puedes crear el habilitador así:

process 
   variable count : natural;
begin
   if rising_edge(50MHz_clk) then
       enable <= '0';
       count := count + 1;
       if count = clock_freq/desired_freq then
          enable <= '1';
          count := 0;
       end if;
    end if;
end process;

crea un par de constantes con la frecuencia de tu reloj y la frecuencia deseada del habilitador y ahí tienes, con un código autoexplicativo.

2voto

Vlagged Puntos 206

Te recomendaría usar Xilinx primitivo generador de reloj digital IP.

Tiene una interfaz gráfica de configuración donde puedes especificar la frecuencia que deseas. Generará un componente con la frecuencia de salida deseada.

Puede encontrarse en el Asistente de IP;

Introducir descripción de la imagen aquí

Y luego podrás especificar la frecuencia que deseas: Introducir descripción de la imagen aquí

1 votos

Esto no es una respuesta correcta para esta aplicación en particular, ya que Clocking Wizard no puede generar una frecuencia tan baja.

0voto

millimoose Puntos 22665

Factor = frecuencia de señal de entrada / frecuencia del preescalador de salida.

CE = Habilitación de reloj. Debe ser un pulso de ancho de un reloj (clk) o alto si no se usa.

Q = Señal de salida de un pulso de ancho de un reloj con la frecuencia deseada.

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity preescalador is

  generic (
    FACTOR : integer);

  port (
    clk : in  std_logic;
    rst : in  std_logic;
    CE  : in  std_logic;
    Q   : out std_logic);

end preescalador;

architecture for_prescaler of preescalador is
  signal counter_reg, counter_next : integer range 0 to FACTOR-1;
  signal Q_next: std_logic;
begin  -- for_prescaler

  process (clk, rst)
  begin  -- process
    if rst = '1' then                   -- reinicio asíncrono (activo bajo)
      counter_reg <= 0;
    elsif clk'event and clk = '1' then  -- flanco de subida del reloj
      counter_reg <= counter_next;
    end if;
  end process;

  process (counter_reg, CE)
  begin  -- process
    Q_next <= '0';
     counter_next <= counter_reg;
     if CE = '1' then
        if counter_reg = FACTOR-1  then
          counter_next <= 0;
           Q_next <= '1';
         else
           counter_next <= counter_reg + 1;
        end if;
      end if;
  end process;

  process (clk, rst)
  begin  -- process
    if rst = '1' then                   -- reinicio asíncrono (activo bajo)
      Q <= '0';
    elsif clk'event and clk = '1' then  -- flanco de subida del reloj
      Q <= Q_next;
    end if;
  end process; 

end for_prescaler;

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X