Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VHDL - How should I create a clock in a testbench?

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)

like image 334
alexdavey Avatar asked Jul 28 '13 02:07

alexdavey


2 Answers

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!)

like image 65
Martin Thompson Avatar answered Oct 03 '22 06:10

Martin Thompson


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.

Waveforms for clk_166 and clk_125

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.

like image 45
Morten Zilmer Avatar answered Oct 03 '22 06:10

Morten Zilmer