Aufteilen von 50 MHz auf 2 Hz in VHDL auf Xilinx FPGA


13

Ich habe eine Xilinx-FPGA-Karte mit einem 50-MHz-Kristall. Ich muss das in VHDL auf 2Hz herunter dividieren. Wie mache ich das?


13
Also, was hast du eigentlich versucht?
Matt Young

Warum nicht Xilinx Clock Manager IP verwenden?
Arturs Vancans

Antworten:


19

Grundsätzlich gibt es zwei Möglichkeiten, dies zu tun. Der erste ist die Verwendung des nativen Xilinx-Clock-Synthesizer-Kerns. Dies hat unter anderem den Vorteil, dass die Xlinx-Tools die Uhr als solche erkennen und über die erforderlichen Pfade leiten. Die Tools werden auch alle zeitlichen Einschränkungen bewältigen (in diesem Fall nicht wirklich zutreffend, da es sich um einen 2-Hz-Takt handelt)

Die zweite Möglichkeit besteht darin, einen Zähler zu verwenden, um die Anzahl der schnelleren Taktimpulse zu zählen, bis die Hälfte Ihrer langsameren Taktperiode verstrichen ist. In Ihrem Fall beträgt die Anzahl der schnellen Taktimpulse, die eine Taktperiode eines langsamen Taktzyklus ausmachen, beispielsweise 50000000/2 = 25000000. Da wir eine halbe Taktperiode wünschen, sind dies 25000000/2 = 12500000 für jeden Halbzyklus . (die Dauer jedes Hochs oder Tiefs).

So sieht es in VHDL aus:

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;

Dinge zu beachten:

  • Der erzeugte Takt ist beim Reset Null. Dies ist für einige Anwendungen in Ordnung und für andere nicht. Es kommt nur darauf an, wofür Sie die Uhr benötigen.
  • Der erzeugte Takt wird von den Xilinx-Synthesewerkzeugen als normales Signal weitergeleitet.
  • 2Hz ist sehr langsam. Das Simulieren für eine Sekunde wird eine Weile dauern. Da es sich um eine kleine Codemenge handelt, sollte die Simulation auch für 1 Sekunde relativ schnell erfolgen. Wenn Sie jedoch mit dem Hinzufügen von Code beginnen, kann die Zeit für die Simulation eines Taktzyklus von 2 Hz erheblich lang sein.

EDIT: clk_2Hz_i dient zum Puffern des Ausgangssignals. VHDL verwendet kein Signal rechts von einer Zuweisung, wenn es auch ein Ausgang ist.


1
Nicht schlecht, aber Sie können unsigned mit integer hinzufügen / vergleichen, also: if prescaler = 50_000_000/4 then ...und prescaler <= prescaler + 1;wäre ein bisschen einfacher.
Brian Drummond

@StaceyAnne Wenn ich das versuche, erhalte ich die Meldung "Aus Objekt clk_o kann nicht gelesen werden; 'buffer' oder 'inout' verwenden". Habe ich etwas verpasst?
Ausweichen

@evading, ein Puffer am Ausgang wird benötigt. VHDL mag die Tatsache nicht, dass clk_2Hzes sich um eine Ausgabe handelt, aber ihr Wert wird in dieser Zeile gelesen clk_2Hz <= not clk_2Hz;. Ich habe im Update bearbeitet.
Stanri

+1 Tolles Beispiel. Aber hier zeigt sich meine Unwissenheit (neu bei VHDL). Was ist der Unterschied zwischen prescaler <= (others => '0');und prescaler <= '0';?
Cbmeeks

NVM! Ich habe total vermisst, wofür othersich ein VHDL-Buch gelesen habe. Es ist nur eine Abkürzung, um alle "anderen" Bits auf einen gemeinsamen Wert zu deklarieren, anstatt so etwas wie "0000000000000000 ..." zu verwenden.
cbmeeks

9

Verwenden Sie einen Clock Prescaler.

Ihr Vorteilswert ist Ihre (clock_speed / desired_clock_speed) / 2, also (50Mhz (50,000,000) / 2hz (2)) / 2 = 12,500,000, was in binär 101111101011110000100000 wäre.

Einfacher ausgedrückt : (50.000.000) / 2) / 2 = 12.500.000 In Binär umwandeln -> 101111101011110000100000

Hier ist ein Code, wie Sie vorgehen müssen: Verwenden Sie newClock für alles, wofür Sie 2 Hz benötigen ...

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;

Es scheint, als würden Sie zwei Takte erzeugen, einen mit 0,5 Hz und einen mit 1 Hz? (seit deiner uhrzeit ist dein prescaler * 2?). Außerdem gibt das "+" einen Fehler aus, da Sie slvs hinzufügen, und ich bin mir nicht sicher, ob ich die Überlaufeigenschaft von add auf diese Weise verwenden soll. warum nicht einfach gehen newClock : std_logic := '0', bis zu prescaler / 2 zählen und zuweisen newClk <= not newClk?
Stanri

Danke, meine Logik war ein bisschen abwegig. Ich habe meinen ersten Beitrag jetzt mit getestetem Code und ein paar Ihrer Vorschläge aktualisiert :)
MLM

Ugh - all diese Einsen und Nullen und ein Kommentar, um zu sagen, was es wirklich ist! Warum nicht den Compiler benutzen, um das für dich zu tun ??? Und warum nicht einfach ganze Zahlen verwenden?
Martin Thompson

Ich kann mich irren, aber ich denke, dass die Verwendung von Standardwerten bei der Definition von Signalen in einer Architektur wie ": = (others => '0')" nicht synthetisierbar ist.
Arturs Vancans

Es ist synthetisierbar, funktioniert aber grundsätzlich nur auf SRAM-basierten FPGAs, wie die meisten von Xilinx, Altera oder Lattice.
Yann Vernier

8

Normalerweise möchten Sie eigentlich nichts Langsames takten, erstellen Sie einfach eine Freigabe mit der richtigen Rate und verwenden Sie diese in der Logik:

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

Sie können die Freigabe folgendermaßen erstellen:

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;

Erstellen Sie ein paar Konstanten mit Ihrer Taktfrequenz und der gewünschten Aktivierungsfrequenz, und schon können Sie mit selbstdokumentierendem Code starten.


3

Ich würde eher vorschlagen, mit Xilinx Primitice Digital Clock Manager IP .

Es verfügt über eine grafische Einstellungsoberfläche, in der Sie die gewünschte Frequenz festlegen können. Es wird eine Komponente mit Ihrem gewünschten Ausgang als Frequenz erzeugt.

Es befindet sich im IP-Assistenten.

Bildbeschreibung hier eingeben

Und dann können Sie angeben, welche Frequenz Sie möchten: Bildbeschreibung hier eingeben


0

Faktor = Input-Signal-Frequenz / Output-Prescaler-Frequenz.

CE = Clock Enable. Es sollte ein eintaktiger (clk) breiter Impuls sein oder hoch, wenn er nicht verwendet wird.

Q = Ausgangssignal eines taktbreiten Impulses mit der gewünschten Frequenz.

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;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.