Ich habe eine Xilinx-FPGA-Karte mit einem 50-MHz-Kristall. Ich muss das in VHDL auf 2Hz herunter dividieren. Wie mache ich das?
Ich habe eine Xilinx-FPGA-Karte mit einem 50-MHz-Kristall. Ich muss das in VHDL auf 2Hz herunter dividieren. Wie mache ich das?
Antworten:
Grundsätzlich gibt es zwei Möglichkeiten, dies zu tun. Der erste ist die Verwendung des nativen Xilinx-Clock-Synthesizer-Kerns. Dies hat unter anderem den Vorteil, dass die Xlinx-Tools die Uhr als solche erkennen und über die erforderlichen Pfade leiten. Die Tools werden auch alle zeitlichen Einschränkungen bewältigen (in diesem Fall nicht wirklich zutreffend, da es sich um einen 2-Hz-Takt handelt)
Die zweite Möglichkeit besteht darin, einen Zähler zu verwenden, um die Anzahl der schnelleren Taktimpulse zu zählen, bis die Hälfte Ihrer langsameren Taktperiode verstrichen ist. In Ihrem Fall beträgt die Anzahl der schnellen Taktimpulse, die eine Taktperiode eines langsamen Taktzyklus ausmachen, beispielsweise 50000000/2 = 25000000. Da wir eine halbe Taktperiode wünschen, sind dies 25000000/2 = 12500000 für jeden Halbzyklus . (die Dauer jedes Hochs oder Tiefs).
So sieht es in VHDL aus:
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;
Dinge zu beachten:
EDIT: clk_2Hz_i dient zum Puffern des Ausgangssignals. VHDL verwendet kein Signal rechts von einer Zuweisung, wenn es auch ein Ausgang ist.
if prescaler = 50_000_000/4 then ...
und prescaler <= prescaler + 1;
wäre ein bisschen einfacher.
clk_2Hz
es sich um eine Ausgabe handelt, aber ihr Wert wird in dieser Zeile gelesen clk_2Hz <= not clk_2Hz;
. Ich habe im Update bearbeitet.
prescaler <= (others => '0');
und prescaler <= '0';
?
others
ich ein VHDL-Buch gelesen habe. Es ist nur eine Abkürzung, um alle "anderen" Bits auf einen gemeinsamen Wert zu deklarieren, anstatt so etwas wie "0000000000000000 ..." zu verwenden.
Verwenden Sie einen Clock Prescaler.
Ihr Vorteilswert ist Ihre (clock_speed / desired_clock_speed) / 2, also (50Mhz (50,000,000) / 2hz (2)) / 2 = 12,500,000, was in binär 101111101011110000100000 wäre.
Einfacher ausgedrückt : (50.000.000) / 2) / 2 = 12.500.000 In Binär umwandeln -> 101111101011110000100000
Hier ist ein Code, wie Sie vorgehen müssen: Verwenden Sie newClock für alles, wofür Sie 2 Hz benötigen ...
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'
, bis zu prescaler / 2 zählen und zuweisen newClk <= not newClk
?
Normalerweise möchten Sie eigentlich nichts Langsames takten, erstellen Sie einfach eine Freigabe mit der richtigen Rate und verwenden Sie diese in der Logik:
if rising_edge(50MHz_clk) and enable = '1' then
Sie können die Freigabe folgendermaßen erstellen:
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;
Erstellen Sie ein paar Konstanten mit Ihrer Taktfrequenz und der gewünschten Aktivierungsfrequenz, und schon können Sie mit selbstdokumentierendem Code starten.
Ich würde eher vorschlagen, mit Xilinx Primitice Digital Clock Manager IP .
Es verfügt über eine grafische Einstellungsoberfläche, in der Sie die gewünschte Frequenz festlegen können. Es wird eine Komponente mit Ihrem gewünschten Ausgang als Frequenz erzeugt.
Es befindet sich im IP-Assistenten.
Und dann können Sie angeben, welche Frequenz Sie möchten:
Faktor = Input-Signal-Frequenz / Output-Prescaler-Frequenz.
CE = Clock Enable. Es sollte ein eintaktiger (clk) breiter Impuls sein oder hoch, wenn er nicht verwendet wird.
Q = Ausgangssignal eines taktbreiten Impulses mit der gewünschten Frequenz.
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;