Xilinx FPGA'da VHDL'de 50MHz'yi 2Hz'e nasıl bölerim


13

50MHz kristalli bir Xilinx FPGA kartım var. VHDL'de bunu 2Hz'e bölmem gerekiyor. Bunu nasıl yaparım?


13
Peki ne denediniz?
Matt Young

Neden Xilinx saat yöneticisi IP'sini kullanmıyorsunuz?
Arturs Vancans

Yanıtlar:


19

Temel olarak, bunu yapmanın iki yolu vardır. Birincisi Xilinx doğal saat sentezleyici çekirdeğini kullanmaktır. Bunun avantajlarından biri, Xlinx araçlarının saati bu şekilde tanıması ve gerekli yollardan geçirmesidir. Araçlar ayrıca zamanlama kısıtlamalarını da ele alacaktır (2Hz saat olduğu için bu durumda gerçekten geçerli değildir)

İkinci yol, daha yavaş saat sürenizin yarısı geçene kadar daha hızlı saat darbelerinin sayısını saymak için bir sayaç kullanmaktır. Örneğin, sizin durumunuz için, yavaş saat döngüsünün bir saat periyodunu oluşturan hızlı saat darbelerinin sayısı 50000000/2 = 25000000'dür. Yarım saat periyodu istediğimizden, her yarım döngü için 25000000/2 = 12500000'dir. . (her bir yüksek veya düşük süre).

VHDL'de şöyle görünüyor:

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;

Dikkat edilmesi gerekenler:

  • Sıfırlama sırasında üretilen saat sıfırdır. Bu, bazı uygulamalar için uygundur ve diğerleri için değil, sadece saat için neye ihtiyacınız olduğuna bağlıdır.
  • Oluşturulan saat, Xilinx sentez araçları tarafından normal bir sinyal olarak yönlendirilecektir.
  • 2Hz çok yavaş. Bir saniyeliğine simülasyon yapmak biraz zaman alacak. Küçük bir kod miktarıdır, bu nedenle 1 saniye boyunca bile benzetim yapmak nispeten hızlı olmalıdır, ancak kod eklemeye başlarsanız, 2 Hz'lik bir saat döngüsünü simüle etmek için geçen süre önemli ölçüde uzun olabilir.

EDIT: clk_2Hz_i çıkış sinyalini tamponlamak için kullanılır. VHDL, aynı zamanda bir çıktı olduğunda ödevin sağında bir sinyal kullanmaktan hoşlanmaz.


1
Kötü değil, ama imzasız tamsayı ekleyebilir / karşılaştırabilirsiniz, bu yüzden: if prescaler = 50_000_000/4 then ...ve prescaler <= prescaler + 1;biraz daha basit olurdu.
Brian Drummond

@StaceyAnne Bunu denemek, "clk_o 'out' nesnesinden okunamıyor; 'buffer' veya 'inout' kullanın" bir şey mi kaçırdım?
kaçarak

@evading, çıktı üzerinde bir tampon gereklidir. VHDL clk_2Hzbir çıktı olduğu gerçeğini sevmez , ancak değeri bu satırda okunmaktadır clk_2Hz <= not clk_2Hz;. Düzeltmeyi düzenledim.
stanri

+1 Harika örnek. Ama burada cehaletim gösteriyor (VHDL için yeni). Arasındaki fark nedir prescaler <= (others => '0');ve prescaler <= '0';?
cbmeeks

NVM! Sahip olduğum othersbir VHDL kitabını okurken ne için kullanıldığını tamamen özledim . Bu, "000000000000000000 ...." gibi bir şey kullanmak yerine, tüm "diğer" bitleri ortak bir değere bildirmek için kullanılan bir kısayoldur.
cbmeeks

9

Bir saat ön ölçekleyici kullanın.

Ön ölçekleyici değeriniz (clock_speed / wanted_clock_speed) / 2 olacak (böylece 50Mhz (50.000.000) / 2hz (2)) / 2 = 12.500.000 olacak ve bu da ikili dosyada 101111101011110000100000 olacaktır.

Daha basit: (50.000.000) / 2) / 2 = 12.500.000 ikiliye dönüştür -> 101111101011110000100000

İşte yapmanız gerekenler için bazı kodlar: 2hz için ihtiyacınız olan her şey için newClock kullanın ...

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;

Görünüşe göre biri 0.5 Hz biri 1 Hz biri olmak üzere iki saat mi üretiyorsunuz? (saat süreniz ön ölçekleyiciniz * 2 olduğundan?). Ayrıca, slvs eklediğiniz için "+" hata verecektir ve her durumda eklentinin taşma özelliğini bu şekilde kullanma konusunda emin değilim. neden sadece gitmiyor newClock : std_logic := '0', ölçeklendiriciye / 2'ye kadar sayın ve atayın newClk <= not newClk?
stanri

Teşekkürler, mantığım biraz kapandı. İlk yazımı şimdi test edilmiş bazı kodlarla ve önerilerinizden birkaçı ile güncelledim :)
MLM

Ugh - tüm bu olanlar ve sıfırlar ve gerçekten ne olduğunu söylemek için bir yorum! Neden derleyici bunu sizin için yapmak ??? Ve neden sadece tamsayıları kullanmıyorsunuz?
Martin Thompson

Yanlış olabilirim, ancak mimaride sinyalleri ": = (diğerleri => '0')" gibi tanımlarken varsayılan değerleri kullanmak sentezlenemez.
Arturs Vancans

Sentezlenebilir, ancak temelde sadece Xilinx, Altera veya Lattice gibi SRAM tabanlı FPGA'larda çalışır.
Yann Vernier

8

Genellikle yavaş olan hiçbir şeyi izlemek istemezsiniz, sadece doğru oranda bir izin oluşturun ve bunu mantıkta kullanın:

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

etkinleştirmeyi şu şekilde oluşturabilirsiniz:

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;

önyükleme için kendi kendini belgeleyen kodla, saat frekansınız ve istediğiniz etkinleştirme sıklığınız ve gideceğiniz uzaklıkla birkaç sabit oluşturun.


3

Ben Xilinx primitice dijital saat yöneticisi IP kullanmanızı öneririm .

İstediğiniz frekansı belirleyebileceğiniz grafik ayarları arabirimine sahiptir. Frekans olarak istediğiniz çıktıya sahip bir bileşen oluşturur.

IP Sihirbazı'nda bulunabilir;

resim açıklamasını buraya girin

Ve sonra hangi frekansı istediğinizi belirtebilirsiniz: resim açıklamasını buraya girin


0

Faktör = giriş-sinyal-frekans / çıkış-ölçekleyici-frekans.

CE = Saat Etkinleştir. Bir saat (clk) genişliğinde darbe veya kullanılmıyorsa yüksek olmalıdır.

Q = İstenen frekansta bir saat genişliğinde darbenin çıkış sinyali.

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.