Comment créer une horloge dans un banc d'essai? J'ai déjà trouvé une réponse, mais d'autres sur le débordement de pile ont suggéré qu'il existe des moyens alternatifs ou meilleurs d'y parvenir:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY test_tb IS
END test_tb;
ARCHITECTURE behavior OF test_tb IS
COMPONENT test
PORT(clk : IN std_logic;)
END COMPONENT;
signal clk : std_logic := '0';
constant clk_period : time := 1 ns;
BEGIN
uut: test PORT MAP (clk => clk);
-- Clock process definitions( clock with 50% duty cycle is generated here.
clk_process :process
begin
clk <= '0';
wait for clk_period/2; --for 0.5 ns signal is '0'.
clk <= '1';
wait for clk_period/2; --for next 0.5 ns signal is '1'.
end process;
END;
(source ici )
Ma technique préférée:
signal clk : std_logic := '0'; -- make sure you initialise!
...
clk <= not clk after half_period;
J'étends généralement cela avec un signal finished
pour me permettre d'arrêter l'horloge:
clk <= not clk after half_period when finished /= '1' else '0';
Alerte Gotcha: Des précautions doivent être prises si vous calculez half_period
à partir d'une autre constante en divisant par 2. Le simulateur a un paramètre de "résolution temporelle", qui par défaut est souvent nanosecondes ... Dans ce cas, 5 ns / 2
sort pour être 2 ns
donc vous vous retrouvez avec une période de 4ns! Réglez le simulateur en picosecondes et tout ira bien (jusqu'à ce que vous ayez besoin de fractions de picoseconde pour représenter votre temps d'horloge de toute façon!)
Si plusieurs horloges sont générées avec des fréquences différentes, la génération d'horloge peut être simplifiée si une procédure est appelée en tant qu'appel de procédure simultanée. Le problème de résolution du temps, mentionné par Martin Thompson, peut être atténué un peu en utilisant différents temps hauts et bas dans la procédure. Le banc d'essai avec procédure de génération d'horloge est:
library ieee;
use ieee.std_logic_1164.all;
entity tb is
end entity;
architecture sim of tb is
-- Procedure for clock generation
procedure clk_gen(signal clk : out std_logic; constant FREQ : real) is
constant PERIOD : time := 1 sec / FREQ; -- Full period
constant HIGH_TIME : time := PERIOD / 2; -- High time
constant LOW_TIME : time := PERIOD - HIGH_TIME; -- Low time; always >= HIGH_TIME
begin
-- Check the arguments
assert (HIGH_TIME /= 0 fs) report "clk_plain: High time is zero; time resolution to large for frequency" severity FAILURE;
-- Generate a clock cycle
loop
clk <= '1';
wait for HIGH_TIME;
clk <= '0';
wait for LOW_TIME;
end loop;
end procedure;
-- Clock frequency and signal
signal clk_166 : std_logic;
signal clk_125 : std_logic;
begin
-- Clock generation with concurrent procedure call
clk_gen(clk_166, 166.667E6); -- 166.667 MHz clock
clk_gen(clk_125, 125.000E6); -- 125.000 MHz clock
-- Time resolution show
assert FALSE report "Time resolution: " & time'image(time'succ(0 fs)) severity NOTE;
end architecture;
La résolution temporelle est imprimée sur le terminal pour information, en utilisant la dernière assertion simultanée dans le banc de test.
Si la clk_gen
la procédure est placée dans un emballage séparé, puis la réutilisation de banc d'essai à banc d'essai devient simple.
La forme d'onde des horloges est illustrée dans la figure ci-dessous.
Un générateur d'horloge plus avancé peut également être créé dans la procédure, qui peut ajuster la période au fil du temps pour correspondre à la fréquence demandée malgré la limitation par la résolution temporelle. Ceci est montré ici:
-- Advanced procedure for clock generation, with period adjust to match frequency over time, and run control by signal
procedure clk_gen(signal clk : out std_logic; constant FREQ : real; PHASE : time := 0 fs; signal run : std_logic) is
constant HIGH_TIME : time := 0.5 sec / FREQ; -- High time as fixed value
variable low_time_v : time; -- Low time calculated per cycle; always >= HIGH_TIME
variable cycles_v : real := 0.0; -- Number of cycles
variable freq_time_v : time := 0 fs; -- Time used for generation of cycles
begin
-- Check the arguments
assert (HIGH_TIME /= 0 fs) report "clk_gen: High time is zero; time resolution to large for frequency" severity FAILURE;
-- Initial phase shift
clk <= '0';
wait for PHASE;
-- Generate cycles
loop
-- Only high Pulse if run is '1' or 'H'
if (run = '1') or (run = 'H') then
clk <= run;
end if;
wait for HIGH_TIME;
-- Low part of cycle
clk <= '0';
low_time_v := 1 sec * ((cycles_v + 1.0) / FREQ) - freq_time_v - HIGH_TIME; -- + 1.0 for cycle after current
wait for low_time_v;
-- Cycle counter and time passed update
cycles_v := cycles_v + 1.0;
freq_time_v := freq_time_v + HIGH_TIME + low_time_v;
end loop;
end procedure;
Encore une fois, la réutilisation via un package sera agréable.
Affectation simultanée des signaux:
library ieee;
use ieee.std_logic_1164.all;
entity foo is
end;
architecture behave of foo is
signal clk: std_logic := '0';
begin
CLOCK:
clk <= '1' after 0.5 ns when clk = '0' else
'0' after 0.5 ns when clk = '1';
end;
ghdl -a foo.vhdl
ghdl -r foo --stop-time = 10ns --wave = foo.ghw
ghdl: info: simulation arrêtée par --stop-time
gtkwave foo.ghw
Les simulateurs simulent des processus et ils seraient transformés en processus équivalent à votre énoncé de processus. Le temps de simulation implique l'utilisation d'attendre ou après lors de la conduite d'événements pour les clauses de sensibilité ou les listes de sensibilité.
Comment utiliser une horloge et faire des assertions
Cet exemple montre comment générer une horloge et donner des entrées et affirmer des sorties pour chaque cycle. Un simple compteur est testé ici.
L'idée clé est que les blocs process
fonctionnent en parallèle, donc l'horloge est générée en parallèle avec les entrées et les assertions.
library ieee;
use ieee.std_logic_1164.all;
entity counter_tb is
end counter_tb;
architecture behav of counter_tb is
constant width : natural := 2;
constant clk_period : time := 1 ns;
signal clk : std_logic := '0';
signal data : std_logic_vector(width-1 downto 0);
signal count : std_logic_vector(width-1 downto 0);
type io_t is record
load : std_logic;
data : std_logic_vector(width-1 downto 0);
count : std_logic_vector(width-1 downto 0);
end record;
type ios_t is array (natural range <>) of io_t;
constant ios : ios_t := (
('1', "00", "00"),
('0', "UU", "01"),
('0', "UU", "10"),
('0', "UU", "11"),
('1', "10", "10"),
('0', "UU", "11"),
('0', "UU", "00"),
('0', "UU", "01")
);
begin
counter_0: entity work.counter port map (clk, load, data, count);
process
begin
for i in ios'range loop
load <= ios(i).load;
data <= ios(i).data;
wait until falling_Edge(clk);
assert count = ios(i).count;
end loop;
wait;
end process;
process
begin
for i in 1 to 2 * ios'length loop
wait for clk_period / 2;
clk <= not clk;
end loop;
wait;
end process;
end behav;
Le compteur ressemblerait à ceci:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; -- unsigned
entity counter is
generic (
width : in natural := 2
);
port (
clk, load : in std_logic;
data : in std_logic_vector(width-1 downto 0);
count : out std_logic_vector(width-1 downto 0)
);
end entity counter;
architecture rtl of counter is
signal cnt : unsigned(width-1 downto 0);
begin
process(clk) is
begin
if rising_Edge(clk) then
if load = '1' then
cnt <= unsigned(data);
else
cnt <= cnt + 1;
end if;
end if;
end process;
count <= std_logic_vector(cnt);
end architecture rtl;
Connexes: https://electronics.stackexchange.com/questions/148320/proper-clock-generation-for-vhdl-testbenches