Як розділити 50 МГц до 2 ГГц в VHDL на Xilinx FPGA


13

У мене є плата Xilinx FPGA з кристалом 50 МГц. Мені потрібно розділити це на 2 ГГц у VHDL. Як це зробити?


13
То що ви насправді пробували?
Метт Янг

Чому б не використовувати IP-менеджер годин Xilinx?
Arturs Vancans

Відповіді:


19

В основному, це два способи. Перший - використання основного ядра синтезатора годинника 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;

Що слід зазначити:

  • Створений годинник дорівнює нулю під час скидання. Для деяких програм це нормально, а не для інших, це просто залежить від того, для чого вам потрібен годинник.
  • Згенерований годинник буде спрямований як звичайний сигнал інструментами синтезу Xilinx.
  • 2 Гц дуже повільно. Моделювання на секунду займе певний час. Це невелика кількість коду, тому його можна порівняно швидко моделювати навіть за 1 секунду, але якщо ви почнете додавати код, час, необхідний для імітації тактового циклу 2 Гц, може бути значно довшим.

EDIT: clk_2Hz_i використовується для буферування вихідного сигналу. VHDL не любить використовувати сигнал праворуч від призначення, коли це також вихід.


1
Непогано, але ви можете додати / порівняти неподписані з цілим числом, так: if prescaler = 50_000_000/4 then ...і prescaler <= prescaler + 1;було б трохи простіше.
Брайан Драммонд

@StaceyAnne Спробувавши це, я отримую "Не вдається прочитати з" out "об'єкта clk_o; використання" buffer "або" inout "" я щось пропустив?
ухилення від

@evading, потрібен буфер на виході. VHDL не подобається, що clk_2Hzце вихід, але все ж його значення читається в цьому рядку clk_2Hz <= not clk_2Hz;. Я відредагував у виправленні.
станрі

+1 Чудовий приклад. Але ось де проявляється моє незнання (нове для VHDL). У чому різниця між prescaler <= (others => '0');і prescaler <= '0';?
cbmeeks

NVM! Я повністю пропустив те, othersдля чого використовував, читаючи книгу про VHDL. Це лише ярлик для оголошення всіх "інших" бітів до загального значення, а не використання чогось типу "000000000000000000 ...." тощо.
cbmeeks

9

Скористайтеся годинником дошка.

Вашим доказовим значенням буде ваш (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;

Здається, ви генеруєте два тактових годин, один - 0,5 Гц і один - 1 Гц? (оскільки ваш часовий період - це ваш prescaler * 2?). Крім того, "+" призведе до помилки, оскільки ви додаєте slvs, і я не дуже впевнений у використанні властивості переповнення додавання таким чином у будь-якому випадку. чому б просто не піти newClock : std_logic := '0', рахувати до prescaler / 2 і призначити newClk <= not newClk?
stanri

Спасибі, моя логіка трохи відключилася. Я оновив свою початкову посаду деяким тестовим кодом зараз і кількома вашими пропозиціями :)
MLM

Фу, всі ці нулі та коментар, щоб сказати, що це насправді! Чому б не використовувати компілятор, щоб зробити це для вас ??? А чому б просто просто не використовувати цілі числа?
Мартін Томпсон

Можливо, я помиляюся, але я думаю, що використання значень за замовчуванням при визначенні сигналів в архітектурі як у ": = (other => '0") "не синтезується.
Arturs Vancans

Це синтезувати, але в основному працює лише на FPGA на базі SRAM, як і більшість Xilinx, Altera або Lattice.
Yann Vernier

8

Зазвичай ви не хочете бачити щось так повільно, просто створіть активацію з правильною швидкістю і використовуйте це в логіці:

 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;

створіть пару констант з тактовою частотою та бажаним увімкненням частоти та подалі від вас, з кодом для самодокументування для завантаження.


3

Я б швидше запропонував використовувати Xilinx primitice цифровий годинниковий менеджер IP .

Він має графічний інтерфейс налаштувань, де ви можете вказати, яку частоту ви хочете. Це генерує компонент із бажаним виходом у якості частоти.

Його можна знайти в майстра IP;

введіть тут опис зображення

І тоді ви зможете вказати, яку частоту ви хочете: введіть тут опис зображення


0

Фактор = частота введення-сигналу-частота / вихід-довірка-частота.

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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.