50MHz kristalli bir Xilinx FPGA kartım var. VHDL'de bunu 2Hz'e bölmem gerekiyor. Bunu nasıl yaparım?
50MHz kristalli bir Xilinx FPGA kartım var. VHDL'de bunu 2Hz'e bölmem gerekiyor. Bunu nasıl yaparım?
Yanıtlar:
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:
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.
if prescaler = 50_000_000/4 then ...
ve prescaler <= prescaler + 1;
biraz daha basit olurdu.
clk_2Hz
bir çıktı olduğu gerçeğini sevmez , ancak değeri bu satırda okunmaktadır clk_2Hz <= not clk_2Hz;
. Düzeltmeyi düzenledim.
prescaler <= (others => '0');
ve prescaler <= '0';
?
others
bir 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.
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;
newClock : std_logic := '0'
, ölçeklendiriciye / 2'ye kadar sayın ve atayın newClk <= not newClk
?
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.
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;
Ve sonra hangi frekansı istediğinizi belirtebilirsiniz:
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;