Jak podzielić 50 MHz do 2 Hz w VHDL na Xilinx FPGA

13

Mam płytkę Xilinx FPGA z kryształem 50 MHz. Muszę podzielić to do 2 Hz w VHDL. Jak mam to zrobic?

ABAYOMI STEPHEN
źródło
13
Więc czego tak naprawdę próbowałeś?
Matt Young
Dlaczego nie skorzystać z IP menedżera zegara Xilinx?
Arturs Vancans

Odpowiedzi:

19

Zasadniczo istnieją dwa sposoby na zrobienie tego. Pierwszym jest użycie natywnego rdzenia syntezatora zegara Xilinx. Jedną z zalet tego jest to, że narzędzia Xlinx rozpoznają zegar jako taki i kierują go wymaganymi ścieżkami. Narzędzia poradzą sobie również z wszelkimi ograniczeniami czasowymi (tak naprawdę nie ma zastosowania w tym przypadku, ponieważ jest to zegar 2 Hz)

Drugim sposobem jest użycie licznika do zliczenia liczby szybszych impulsów zegara, aż minie połowa wolniejszego okresu zegara. Na przykład w twoim przypadku liczba szybkich impulsów zegarowych, które składają się na jeden okres taktowania wolnego cyklu zegarowego, wynosi 50000000/2 = 25000000. Ponieważ chcemy pół okresu zegarowego, to 25000000/2 = 12500000 dla każdego półcyklu . (czas trwania każdego maksimum lub minimum).

Oto jak to wygląda w 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;

Ważne uwagi:

  • Wygenerowany zegar ma wartość zero podczas resetowania. Jest to odpowiednie dla niektórych aplikacji, a nie dla innych, zależy tylko od tego, do czego potrzebujesz zegara.
  • Wygenerowany zegar będzie kierowany jako normalny sygnał przez narzędzia do syntezy Xilinx.
  • 2 Hz jest bardzo wolne. Symulacja na sekundę zajmie trochę czasu. Jest to niewielka ilość kodu, więc symulacja powinna być stosunkowo szybka nawet przez 1 sekundę, ale jeśli zaczniesz dodawać kod, czas potrzebny do symulacji cyklu zegara 2 Hz może być znacznie dłuższy.

EDYCJA: clk_2Hz_i służy do buforowania sygnału wyjściowego. VHDL nie lubi używać sygnału po prawej stronie przypisania, gdy jest to również wyjście.

stanri
źródło
1
Nieźle, ale możesz dodać / porównać bez znaku z liczbą całkowitą, więc: if prescaler = 50_000_000/4 then ...i prescaler <= prescaler + 1;byłoby trochę prostsze.
Brian Drummond
@StaceyAnne Próbując tego, otrzymuję komunikat „Nie mogę odczytać z„ wyjściowego ”obiektu clk_o; użyć„ bufora ”lub„ wejściowego ”czy coś przeoczyłem?
unikając
@ evading, potrzebny jest bufor na wyjściu. VHDL nie podoba się fakt, że clk_2Hzjest to wynik, ale jego wartość jest odczytywana w tym wierszu clk_2Hz <= not clk_2Hz;. Edytowałem poprawkę.
stanri
+1 Świetny przykład. Ale tutaj pokazuje moja ignorancja (nowość w VHDL). Jaka jest różnica między prescaler <= (others => '0');i prescaler <= '0';?
cbmeeks
NVM! Zupełnie tęskniłem za tym, co othersbyło używane podczas czytania mojej książki VHDL. To tylko skrót do zadeklarowania wszystkich „innych” bitów do wspólnej wartości zamiast używania czegoś takiego jak „000000000000000000 ....” itd.
cbmeeks
9

Użyj zegarowego preskalera.

Twoja wartość preskalera będzie wynosić (prędkość_zegara / pożądana_godzina) / 2, więc (50 MHz (50 000 000) / 2 Hz (2)) / 2 = 12 500 000, co w przypadku binarnej wartości to 101111101011110000100000.

Mówiąc prościej: (50 000 000) / 2) / 2 = 12 500 000 konwersji na binarne -> 101111101011110000100000

Oto kod tego, co należy zrobić: Użyj newClock do wszystkiego, czego potrzebujesz 2 Hz dla ...

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
źródło
Wygląda na to, że generujesz dwa zegary, jeden o częstotliwości 0,5 Hz i jeden o częstotliwości 1 Hz? (skoro twój okres zegarowy to twój prescaler * 2?). Również „+” spowoduje błąd, ponieważ dodajesz slvs, a ja nie jestem pewien, czy w każdym razie użyję w ten sposób właściwości przepełnienia dodatku. dlaczego nie pójść newClock : std_logic := '0', policzyć do prescaler / 2 i przypisać newClk <= not newClk?
stanri
Dzięki, moja logika była trochę niepoprawna. Zaktualizowałem swój początkowy post przy pomocy przetestowanego kodu i kilku waszych sugestii :)
MLM
Ugh - wszystkie te zera i zera oraz komentarz do powiedzenia, co to naprawdę jest! Dlaczego nie skorzystać z kompilatora, aby zrobić to za Ciebie? I dlaczego by nie użyć liczb całkowitych?
Martin Thompson,
Mogę się mylić, ale myślę, że używanie wartości domyślnych przy definiowaniu sygnałów w architekturze jak w „: = (inni => '0')” nie jest możliwe do syntezy.
Arturs Vancans
Jest syntezowalny, ale w zasadzie działa tylko na FPGA opartych na SRAM, jak większość z Xilinx, Altera lub Lattice.
Yann Vernier
8

Zazwyczaj nie chcesz taktować tak wolno, po prostu utwórz zezwolenie z właściwą szybkością i użyj tego w logice:

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

możesz utworzyć włączenie w ten sposób:

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;

utwórz kilka stałych na podstawie częstotliwości zegara i żądanej częstotliwości włączania i gotowe, korzystając z samokontrującego się kodu.

Martin Thompson
źródło
3

Raczej sugerowałbym użycie IP cyfrowego menedżera zegara Xilinx primitice .

Ma interfejs ustawień graficznych, w którym można określić żądaną częstotliwość. Wygeneruje komponent o pożądanej mocy wyjściowej jako częstotliwości.

Można go znaleźć w Kreatorze IP;

wprowadź opis zdjęcia tutaj

A potem będziesz mógł określić, jakiej częstotliwości chcesz: wprowadź opis zdjęcia tutaj

Arturs Vancans
źródło
0

Współczynnik = częstotliwość sygnału wejściowego / częstotliwość wyjściowa preskalera.

CE = Włącz zegar. Powinien to być impuls o szerokości jednego zegara (clk) lub wysoki, jeśli nie jest używany.

Q = Sygnał wyjściowy impulsu o szerokości jednego zegara z żądaną częstotliwością.

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
źródło