Как разделить 50 МГц до 2 Гц в VHDL на Xilinx FPGA


13

У меня есть плата Xilinx FPGA с кристаллом 50 МГц. Мне нужно разделить это до 2 Гц в VHDL. Как мне это сделать?


13
Так что вы на самом деле пытались?
Мэтт Янг

Почему бы не использовать Xilinx Clock Manager IP?
Артурс 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
Неплохо, но вы можете добавить / сравнить unsigned с целым числом, так что: if prescaler = 50_000_000/4 then ...и prescaler <= prescaler + 1;будет немного проще.
Брайан Драммонд

@StaceyAnne Пытаясь это, я получаю "Не могу прочитать из объекта" out "clk_o; использовать" буфер "или" 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 преобразовать в двоичный файл -> 101111101011110000100000

Вот некоторый код того, что нужно сделать: используйте 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 Гц? (так как ваш тактовый период - ваш прескалер * 2?). Кроме того, «+» выдаст ошибку, так как вы добавляете slvs, и я не очень уверен в том, чтобы использовать свойство переполнения add таким образом в любом случае. почему бы просто не пойти newClock : std_logic := '0', сосчитать до prescaler / 2 и назначить newClk <= not newClk?
Станри

Спасибо, моя логика была немного не в порядке. Я обновил свой первоначальный пост с некоторым протестированным кодом и несколькими вашими предложениями :)
MLM

Тьфу - все эти нули и комментарий, чтобы сказать, что это на самом деле! Почему бы не использовать компилятор, чтобы сделать это для вас ??? И почему бы просто не использовать целые числа?
Мартин Томпсон

Возможно, я ошибаюсь, но я думаю, что использование значений по умолчанию при определении сигналов в архитектуре, таких как ": = (others => '0')", невозможно синтезировать.
Артурс Vancans

Он синтезируемый, но в основном работает только на FPGA на основе SRAM, как и большинство из Xilinx, Altera или Lattice.
Ян Вернье

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

Я бы предпочел использовать IP-менеджер менеджера цифровых часов Xilinx primitice .

Он имеет графический интерфейс настроек, где вы можете указать, какую частоту вы хотите. Он сгенерирует компонент с желаемым выходом в качестве частоты.

Его можно найти в мастере IP;

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

И тогда вы сможете указать, какую частоту вы хотите: введите описание изображения здесь


0

Фактор = входной сигнал-частота / выходной прескейлер-частота.

CE = часы включены. Это должен быть импульс длительностью один такт (clk) или высокий, если он не используется.

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.