Comment diviser 50 MHz à 2 Hz en VHDL sur Xilinx FPGA

13

J'ai une carte FPGA Xilinx, avec un cristal 50 MHz. Je dois diviser cela à 2 Hz en VHDL. Comment puis-je faire cela?

ABAYOMI STEPHEN
la source
13
Alors qu'avez-vous réellement essayé?
Matt Young
Pourquoi ne pas utiliser l'IP du gestionnaire d'horloge Xilinx?
Arturs Vancans

Réponses:

19

Fondamentalement, il existe deux façons de procéder. La première consiste à utiliser le cœur du synthétiseur d'horloge natif Xilinx. L'un des avantages de ceci est que les outils Xlinx reconnaîtront l'horloge en tant que telle et la dirigeront à travers les voies requises. Les outils géreront également toutes les contraintes de synchronisation (pas vraiment applicables dans ce cas, car il s'agit d'une horloge de 2 Hz)

La deuxième façon consiste à utiliser un compteur pour compter le nombre d'impulsions d'horloge plus rapides jusqu'à ce que la moitié de votre période d'horloge plus lente se soit écoulée. Par exemple, pour votre cas, le nombre d'impulsions d'horloge rapide qui composent une période d'horloge d'un cycle d'horloge lente est 50000000/2 = 25000000. Puisque nous voulons une demi-période d'horloge, c'est 25000000/2 = 12500000 pour chaque demi-cycle . (la durée de chaque haut ou bas).

Voici à quoi cela ressemble en 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;

A noter:

  • L'horloge générée est nulle lors de la réinitialisation. C'est correct pour certaines applications, et pas pour d'autres, cela dépend juste de ce dont vous avez besoin pour l'horloge.
  • L'horloge générée sera acheminée en tant que signal normal par les outils de synthèse Xilinx.
  • 2Hz est très lent. Simuler une seconde va prendre un certain temps. C'est une petite quantité de code, donc il devrait être relativement rapide de simuler même pendant 1 seconde, mais si vous commencez à ajouter du code, le temps nécessaire pour simuler un cycle d'horloge de 2 Hz pourrait être considérablement long.

EDIT: clk_2Hz_i est utilisé pour mettre en mémoire tampon le signal de sortie. VHDL n'aime pas utiliser un signal à droite d'une affectation lorsqu'il s'agit également d'une sortie.

Stanri
la source
1
Pas mal, mais vous pouvez ajouter / comparer non signé avec un entier, donc: if prescaler = 50_000_000/4 then ...et ce prescaler <= prescaler + 1;serait un peu plus simple.
Brian Drummond
@StaceyAnne En essayant cela, j'obtiens "Impossible de lire à partir de l'objet 'out' clk_o; utilisez 'buffer' ou 'inout'" ai-je manqué quelque chose?
éluder le
@evading, un tampon sur la sortie est nécessaire. VHDL n'aime pas le fait qu'il clk_2Hzs'agit d'une sortie, mais pourtant sa valeur est lue sur cette ligne clk_2Hz <= not clk_2Hz;. J'ai édité le correctif.
stanri
+1 Excellent exemple. Mais c'est là que mon ignorance montre (nouveau sur VHDL). Quelle est la difference entre prescaler <= (others => '0');et prescaler <= '0';?
cbmeeks
NVM! J'ai totalement raté ce qui othersétait utilisé lors de la lecture d'un livre VHDL que j'ai. C'est juste un raccourci pour déclarer tous les "autres" bits à une valeur commune au lieu d'utiliser quelque chose comme "000000000000000000 ....", etc.
cbmeeks
9

Utilisez un détartreur d'horloge.

Votre valeur de pré-échelle sera votre (vitesse_horloge / vitesse_horloge souhaitée) / 2 donc (50Mhz (50,000,000) / 2hz (2)) / 2 = 12,500,000 qui en binaire serait 1011111010111100000000000.

Plus simplement: (50 000 000) / 2) / 2 = 12 500 000 converti en binaire -> 101111101011110000100000

Voici un code de quoi faire: Utilisez newClock pour tout ce dont vous avez besoin de 2hz pour ...

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;
MLM
la source
Il semble que vous génériez deux horloges, l'une de 0,5 Hz et l'autre de 1 Hz? (puisque votre période d'horloge est votre prescaler * 2?). En outre, le "+" donnera une erreur, car vous ajoutez des slvs, et je ne suis pas sûr d'utiliser la propriété overflow de l'add de cette manière dans tous les cas. pourquoi ne pas simplement aller newClock : std_logic := '0', compter jusqu'à prescaler / 2 et assigner newClk <= not newClk?
stanri
Merci, ma logique était un peu décalée. J'ai mis à jour mon message initial avec du code testé maintenant et quelques-unes de vos suggestions :)
MLM
Ugh - tous ceux et zéros et un commentaire pour dire ce que c'est vraiment! Pourquoi ne pas utiliser le compilateur pour faire ça pour vous ??? Et pourquoi ne pas utiliser des entiers de toute façon?
Martin Thompson
Je peux me tromper, mais je pense que l'utilisation de valeurs par défaut lors de la définition de signaux dans l'architecture comme dans ": = (others => '0')" n'est pas synthétisable.
Arturs Vancans
Il est synthétisable, mais ne fonctionne que sur les FPGA basés sur SRAM, comme la plupart de Xilinx, Altera ou Lattice.
Yann Vernier
8

En général, vous ne voulez pas cadencer quelque chose de lent, créez simplement une activation au taux correct et utilisez-le dans la logique:

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

vous pouvez créer l'activer ainsi:

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;

créez quelques constantes avec votre fréquence d'horloge et la fréquence d'activation souhaitée et vous partez, avec du code auto-documenté pour démarrer.

Martin Thompson
la source
3

Je préférerais plutôt utiliser l' IP du gestionnaire d'horloge numérique de primitice Xilinx .

Il a une interface de paramètres graphiques où vous pouvez spécifier quelle fréquence vous voulez. Il générera un composant avec votre sortie souhaitée comme fréquence.

Il peut être trouvé dans IP Wizard;

entrez la description de l'image ici

Et puis vous pourrez spécifier quelle fréquence voulez-vous: entrez la description de l'image ici

Arturs Vancans
la source
0

Facteur = fréquence-signal-entrée / fréquence-prescaler-sortie.

CE = Horloge activée. Il doit s'agir d'une impulsion large d'une horloge (clk) ou élevée si elle n'est pas utilisée.

Q = Signal de sortie d'une impulsion large d'horloge avec la fréquence souhaitée.

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;
Roger Garzon Nieto
la source