У мене є плата Xilinx FPGA з кристалом 50 МГц. Мені потрібно розділити це на 2 ГГц у VHDL. Як це зробити?
У мене є плата Xilinx FPGA з кристалом 50 МГц. Мені потрібно розділити це на 2 ГГц у VHDL. Як це зробити?
Відповіді:
В основному, це два способи. Перший - використання основного ядра синтезатора годинника Xilinx. Однією з переваг цього є те, що інструменти Xlinx розпізнають годинник як такий і прокладуть його через необхідні шляхи. Інструменти також керуватимуть будь-якими обмеженнями в часі (насправді не застосовується в цьому випадку, оскільки це тактова частота 2 ГГц)
Другий спосіб - використовувати лічильник для підрахунку кількості швидших тактових імпульсів, поки не пройде половина вашого повільнішого тактового періоду. Наприклад, для вашого випадку кількість швидких тактових імпульсів, які складають один тактовий період повільного тактового циклу, становить 50000000/2 = 25000000. Оскільки ми хочемо півгодинного періоду, це 25000000/2 = 12500000 за кожен півцикл . (тривалість кожного високого або низького).
Ось як це виглядає у 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;
Що слід зазначити:
EDIT: clk_2Hz_i використовується для буферування вихідного сигналу. VHDL не любить використовувати сигнал праворуч від призначення, коли це також вихід.
if prescaler = 50_000_000/4 then ...
і prescaler <= prescaler + 1;
було б трохи простіше.
clk_2Hz
це вихід, але все ж його значення читається в цьому рядку clk_2Hz <= not clk_2Hz;
. Я відредагував у виправленні.
prescaler <= (others => '0');
і prescaler <= '0';
?
others
для чого використовував, читаючи книгу про VHDL. Це лише ярлик для оголошення всіх "інших" бітів до загального значення, а не використання чогось типу "000000000000000000 ...." тощо.
Скористайтеся годинником дошка.
Вашим доказовим значенням буде ваш (clock_speed / бажаний_ clock_speed) / 2 так (50 МГц (50 000 000) / 2 Гц (2)) / 2 = 12 500 000, що у двійковій формі буде 101111101011110000100000.
Простіше: (50 000 000) / 2) / 2 = 12 500 000 перетворити на двійкові -> 1011111010111100000000000
Ось код, що робити: використовуйте newClock для всього, що вам потрібно 2 Гц для ...
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'
, рахувати до prescaler / 2 і призначити newClk <= not newClk
?
Зазвичай ви не хочете бачити щось так повільно, просто створіть активацію з правильною швидкістю і використовуйте це в логіці:
if rising_edge(50MHz_clk) and enable = '1' then
ви можете створити активацію таким чином:
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;
створіть пару констант з тактовою частотою та бажаним увімкненням частоти та подалі від вас, з кодом для самодокументування для завантаження.
Я б швидше запропонував використовувати Xilinx primitice цифровий годинниковий менеджер IP .
Він має графічний інтерфейс налаштувань, де ви можете вказати, яку частоту ви хочете. Це генерує компонент із бажаним виходом у якості частоти.
Його можна знайти в майстра IP;
І тоді ви зможете вказати, яку частоту ви хочете:
Фактор = частота введення-сигналу-частота / вихід-довірка-частота.
CE = Увімкнути годинник. Це повинен бути імпульс широким імпульсом у один такт (клік), якщо він не використовується.
Q = вихідний сигнал широкого імпульсу на один тактовий час з потрібною частотою.
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;