How should I create a clock in a testbench? I already have found one answer, however others on stack overflow have suggested that there are alternative or better ways of achieving this:
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 here)
My favoured technique:
signal clk : std_logic := '0'; -- make sure you initialise! ... clk <= not clk after half_period;
I usually extend this with a finished
signal to allow me to stop the clock:
clk <= not clk after half_period when finished /= '1' else '0';
If you use a std_logic
item for your finished
signal can be driven from all the items in your test environment:
signal finished : std_logic; .... stimulus_process:process begin finished <= '0'; drive_various_signals_sync_with_clk; finished <= '1'; end process; monitor_process:process begin finished <= '0'; check_all_signals_until_all_tests_complete; finished <= '1'; end process;
Gotcha alert: Care needs to be taken if you calculate half_period
from another constant by dividing by 2. The simulator has a "time resolution" setting, which often defaults to nanoseconds... In which case, 5 ns / 2
comes out to be 2 ns
so you end up with a period of 4ns! Set the simulator to picoseconds and all will be well (until you need fractions of a picosecond to represent your clock time anyway!)
If multiple clock are generated with different frequencies, then clock generation can be simplified if a procedure is called as concurrent procedure call. The time resolution issue, mentioned by Martin Thompson, may be mitigated a little by using different high and low time in the procedure. The test bench with procedure for clock generation is:
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;
The time resolution is printed on the terminal for information, using the concurrent assert last in the test bench.
If the clk_gen
procedure is placed in a separate package, then reuse from test bench to test bench becomes straight forward.
Waveform for clocks are shown in figure below.
An more advanced clock generator can also be created in the procedure, which can adjust the period over time to match the requested frequency despite the limitation by time resolution. This is shown here:
-- 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;
Again reuse through a package will be nice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With