So teilen Sie 50 MHz in VHDL auf Xilinx FPGA auf 2 Hz herunter

Ich habe ein Xilinx-FPGA-Board mit einem 50-MHz-Quarz. Ich muss das in VHDL auf 2 Hz herunterteilen. Wie mache ich das?

Also, was hast du eigentlich versucht?
Warum nicht Xilinx Clock Manager IP verwenden?

Antworten (5)

Grundsätzlich gibt es zwei Möglichkeiten, dies zu tun. Die erste besteht darin, den nativen Clock-Synthesizer-Kern von Xilinx zu verwenden. Einer der Vorteile davon ist, dass die Xlinx-Tools die Uhr als solche erkennen und sie durch die erforderlichen Pfade leiten. Die Tools behandeln auch alle Zeitbeschränkungen (in diesem Fall nicht wirklich anwendbar, 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 vergangen ist. In Ihrem Fall beträgt die Anzahl schneller Taktimpulse, die eine Taktperiode eines langsamen Taktzyklus ausmachen, beispielsweise 50000000/2 = 25000000. Da wir eine halbe Taktperiode wollen, sind das 25000000/2 = 12500000 für jede Halbperiode . (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 während des Zurücksetzens Null. Dies ist für einige Anwendungen in Ordnung, für andere nicht, es hängt nur davon ab, wofür Sie die Uhr benötigen.
  • Der erzeugte Takt wird von den Xilinx-Synthesewerkzeugen als normales Signal geroutet.
  • 2Hz ist sehr langsam. Das Simulieren für eine Sekunde wird eine Weile dauern. Es handelt sich um eine kleine Codemenge, daher sollte die Simulation selbst für 1 Sekunde relativ schnell sein, aber wenn Sie mit dem Hinzufügen von Code beginnen, kann die Zeit, die zum Simulieren eines Taktzyklus von 2 Hz benötigt wird, erheblich lang sein.

EDIT: clk_2Hz_i wird verwendet, um das Ausgangssignal zu puffern. VHDL mag es nicht, ein Signal rechts von einer Zuweisung zu verwenden, wenn es auch ein Ausgang ist.

Nicht schlecht, aber Sie können unsigned mit integer addieren/vergleichen, also: if prescaler = 50_000_000/4 then ...und prescaler <= prescaler + 1;wäre etwas einfacher.
@StaceyAnne Wenn ich das versuche, bekomme ich "Cannot read from 'out' object clk_o ; use 'buffer' or 'inout'" habe ich etwas verpasst?
@evading, ein Puffer für die Ausgabe wird benötigt. VHDL mag die Tatsache nicht, dass clk_2Hzes sich um eine Ausgabe handelt, aber dennoch wird ihr Wert in dieser Zeile gelesen clk_2Hz <= not clk_2Hz;. Ich habe im Fix bearbeitet.
+1 Tolles Beispiel. Aber hier zeigt sich meine Unwissenheit (neu bei VHDL). Was ist der Unterschied zwischen prescaler <= (others => '0');und prescaler <= '0';?
NVM! Ich habe total vermisst, was othersbeim Lesen eines VHDL-Buches verwendet wurde, das ich habe. Es ist nur eine Abkürzung, um alle "anderen" Bits zu einem gemeinsamen Wert zu deklarieren, anstatt etwas wie "000000000000000000 ..." usw. zu verwenden.

Verwenden Sie einen Clock-Prescaler.

Ihr Prescaler-Wert ist Ihr (clock_speed/desired_clock_speed)/2, also (50 MHz (50.000.000)/2 hz (2))/2 = 12.500.000, was binär 101111101011110000100000 wäre.

Einfacher : (50.000.000)/2)/2 = 12.500.000 in binär umwandeln -> 101111101011110000100000

Hier ist ein Code, was zu tun ist: 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? (da Ihre Taktperiode Ihr Prescaler * 2 ist?). Außerdem gibt das "+" einen Fehler aus, da Sie slvs hinzufügen und ich mir nicht sicher bin, ob ich die Überlaufeigenschaft von add auf jeden Fall auf diese Weise verwenden soll. warum nicht einfach gehen newClock : std_logic := '0', bis prescaler/2 hochzählen und zuweisen newClk <= not newClk?
Danke, meine Logik war etwas daneben. Ich habe meinen ersten Beitrag jetzt mit getestetem Code und einigen Ihrer Vorschläge aktualisiert :)
Ugh - all diese Einsen und Nullen und ein Kommentar, um zu sagen, was es wirklich ist! Warum nicht den Compiler verwenden, um das für Sie zu tun??? Und warum nicht einfach ganze Zahlen verwenden?
Ich kann mich irren, aber ich denke, dass die Verwendung von Standardwerten beim Definieren von Signalen in der Architektur wie in ":= (others => '0')" nicht synthetisierbar ist.
Es ist synthetisierbar, funktioniert aber grundsätzlich nur auf SRAM-basierten FPGAs, wie die meisten von Xilinx, Altera oder Lattice.

Normalerweise möchten Sie eigentlich nichts so langsames takten, erstellen Sie einfach eine Aktivierung 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 so 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 los geht's, mit selbstdokumentierendem Code zum Booten.

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

Es verfügt über eine grafische Einstellungsschnittstelle, auf der Sie die gewünschte Frequenz angeben können. Es erzeugt eine Komponente mit Ihrer gewünschten Ausgabe als Frequenz.

Es kann im IP Wizard gefunden werden;

Geben Sie hier die Bildbeschreibung ein

Und dann können Sie angeben, welche Frequenz Sie möchten:Geben Sie hier die Bildbeschreibung ein

Dies ist keine richtige Antwort für diese spezielle Anwendung, da Clocking Wizard eine so niedrige Frequenz nicht erzeugen kann.

Faktor = Eingangssignal-Frequenz/Ausgangs-Prescaler-Frequenz.

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

Q = Ausgangssignal eines einen Takt breiten 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;