Mam płytkę Xilinx FPGA z kryształem 50 MHz. Muszę podzielić to do 2 Hz w VHDL. Jak mam to zrobic?
Mam płytkę Xilinx FPGA z kryształem 50 MHz. Muszę podzielić to do 2 Hz w VHDL. Jak mam to zrobic?
Odpowiedzi:
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:
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.
if prescaler = 50_000_000/4 then ...
i prescaler <= prescaler + 1;
byłoby trochę prostsze.
clk_2Hz
jest to wynik, ale jego wartość jest odczytywana w tym wierszu clk_2Hz <= not clk_2Hz;
. Edytowałem poprawkę.
prescaler <= (others => '0');
i prescaler <= '0';
?
others
był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.
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;
newClock : std_logic := '0'
, policzyć do prescaler / 2 i przypisać newClk <= not newClk
?
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.
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;
A potem będziesz mógł określić, jakiej częstotliwości chcesz:
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;