Come dividere 50MHz fino a 2Hz in VHDL su Xilinx FPGA


13

Ho una scheda FPGA Xilinx, con un cristallo da 50 MHz. Devo dividerlo fino a 2Hz in VHDL. Come faccio a fare questo?


13
Quindi cosa hai provato?
Matt Young

Perché non usare l'IP del gestore dell'orologio Xilinx?
Arturs Vancans,

Risposte:


19

Fondamentalmente, ci sono due modi per farlo. Il primo è usare il core del sintetizzatore di clock nativo Xilinx. Uno dei vantaggi di questo è che gli strumenti Xlinx riconosceranno l'orologio in quanto tale e lo instraderanno attraverso i percorsi richiesti. Gli strumenti gestiranno anche tutti i vincoli di temporizzazione (non realmente applicabili in questo caso, poiché è un clock a 2Hz)

Il secondo metodo consiste nell'utilizzare un contatore per contare il numero di impulsi di clock più veloci fino a quando non è trascorsa metà del periodo di clock più lento. Ad esempio, nel tuo caso, il numero di impulsi di clock veloce che compongono un periodo di clock di un ciclo di clock lento è 50000000/2 = 25000000. Dato che vogliamo un periodo di mezzo clock, è 25000000/2 = 12500000 per ogni semiciclo . (la durata di ogni massimo o minimo).

Ecco come appare in VHDL:

library IEEE;
use IEEE.STD_LOGIC_1164.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
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  -- process gen_clk
    if rst = '1' then
      clk_2Hz_i   <= '0';
      prescaler   <= (others => '0');
    elsif rising_edge(clk_50Mhz) then   -- rising clock edge
      if prescaler = X"BEBC20" then     -- 12 500 000 in 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;

Cose da notare:

  • L'orologio generato è zero durante il ripristino. Questo va bene per alcune applicazioni, e non per altre, dipende solo da cosa ti serve l'orologio.
  • L'orologio generato verrà indirizzato come un segnale normale dagli strumenti di sintesi di Xilinx.
  • 2Hz è molto lento. La simulazione per un secondo richiederà del tempo. È una piccola quantità di codice, quindi dovrebbe essere relativamente veloce da simulare anche per 1 secondo, ma se inizi ad aggiungere codice, il tempo impiegato per simulare un ciclo di clock di 2 Hz potrebbe essere significativamente lungo.

EDIT: clk_2Hz_i viene utilizzato per bufferizzare il segnale di uscita. A VHDL non piace usare un segnale a destra di un compito quando è anche un'uscita.


1
Non male, ma puoi aggiungere / confrontare senza segno con un numero intero, quindi: if prescaler = 50_000_000/4 then ...e prescaler <= prescaler + 1;sarebbe un po 'più semplice.
Brian Drummond,

@StaceyAnne Provando a questo, ottengo "Impossibile leggere dall'oggetto 'out' clk_o; usare 'buffer' o 'inout'" mi sono perso qualcosa?
eludere il

@evading, è necessario un buffer sull'output. A VHDL non piace il fatto che clk_2Hzsia un output, ma il suo valore viene letto in questa riga clk_2Hz <= not clk_2Hz;. Ho modificato la correzione.
Stanis,

+1 Ottimo esempio. Ma qui è dove la mia ignoranza mostra (nuova a VHDL). Qual è la differenza tra prescaler <= (others => '0');e prescaler <= '0';?
cbmeeks,

NVM! Mi è totalmente mancato ciò che è othersstato usato quando ho letto un libro VHDL. È solo una scorciatoia per dichiarare tutti gli "altri" bit su un valore comune invece di usare qualcosa come "000000000000000000 ....", ecc.
cbmeeks,

9

Usa un orologio prescaler.

Il tuo valore prescaler sarà il tuo (clock_speed / desiderata_clock_speed) / 2 so (50Mhz (50.000.000) / 2hz (2)) / 2 = 12.500.000 che in binario sarebbe 101111101011110000100000.

Più semplicemente: (50.000.000) / 2) / 2 = 12.500.000 convertiti in binari -> 101111101011110000100000

Ecco un codice su cosa fare: usa newClock per qualsiasi cosa tu abbia bisogno di 2hz per ...

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
    -- prescaler should be (clock_speed/desired_clock_speed)/2 because you want a rising edge every period
    signal prescaler: STD_LOGIC_VECTOR(23 downto 0) := "101111101011110000100000"; -- 12,500,000 in binary
    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
                -- Iterate
                newClock <= not newClock;

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


end Behavioral;

Sembra che stai generando due clock, uno da 0,5 Hz e uno da 1 Hz? (poiché il tuo periodo di clock è il tuo prescaler * 2?). Inoltre, "+" genererà un errore, dato che stai aggiungendo slvs, e non sono così sicuro di usare la proprietà di overflow dell'aggiunta in questo modo in ogni caso. perché non semplicemente andare newClock : std_logic := '0', contare fino a prescaler / 2 e assegnare newClk <= not newClk?
Stan

Grazie, la mia logica era un po 'fuori. Ho aggiornato il mio post iniziale con un po 'di codice testato ora e alcuni dei tuoi suggerimenti :)
MLM

Ugh - tutti quelli e zeri e un commento per dire che cos'è veramente! Perché non usare il compilatore per farlo ??? E perché non usare solo numeri interi?
Martin Thompson,

Potrei sbagliarmi, ma penso che l'utilizzo di valori predefiniti quando si definiscono i segnali in architettura come in ": = (others => '0')" non sia sintetizzabile.
Arturs Vancans,

È sintetizzabile, ma fondamentalmente funziona solo su FPGA basati su SRAM, come la maggior parte di Xilinx, Altera o Lattice.
Yann Vernier,

8

Di solito non vuoi davvero sincronizzare nulla di così lento, devi solo creare un'abilitazione alla velocità corretta e usarla nella logica:

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

puoi creare l'abilitazione così:

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 paio di costanti con la frequenza di clock e la frequenza di abilitazione desiderata e parti, con il codice di auto-documentazione per l'avvio.


3

Preferirei suggerire di utilizzare IP Xilinx primitice digital clock manager .

Ha un'interfaccia grafica delle impostazioni in cui è possibile specificare la frequenza desiderata. Genererà un componente con l'uscita desiderata come frequenza.

Può essere trovato nella procedura guidata IP;

inserisci qui la descrizione dell'immagine

E poi sarai in grado di specificare quale frequenza vuoi: inserisci qui la descrizione dell'immagine


0

Fattore = input-segnale-fragranza / output-prescaler-fragranza.

CE = Clock Enable. Dovrebbe essere un impulso di un clock (clk) ampio o alto se non utilizzato.

Q = Segnale di uscita di un impulso ampio di un clock con la frequenza desiderata.

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

entity prescaler is

  generic (
    FACTOR : integer);

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

end prescaler;

architecture for_prescaler of prescaler 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                   -- asynchronous reset (active low)
      counter_reg <= 0;
    elsif clk'event and clk = '1' then  -- rising clock edge
      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                   -- asynchronous reset (active low)
      Q <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
      Q <= Q_next;
    end if;
  end process; 

end for_prescaler;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.