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

后端 未结 4 1431
你的背包
你的背包 2021-01-30 17:57

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 achievi

相关标签:
4条回答
  • 2021-01-30 18:09

    Concurrent signal assignment:

    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 stopped by --stop-time
    gtkwave foo.ghw

    enter image description here

    Simulators simulate processes and it would be transformed into the equivalent process to your process statement. Simulation time implies the use of wait for or after when driving events for sensitivity clauses or sensitivity lists.

    0 讨论(0)
  • 2021-01-30 18:21

    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';
    

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

    0 讨论(0)
  • 2021-01-30 18:23

    How to use a clock and do assertions

    This example shows how to generate a clock, and give inputs and assert outputs for every cycle. A simple counter is tested here.

    The key idea is that the process blocks run in parallel, so the clock is generated in parallel with the inputs and 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;
    

    The counter would look like this:

    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;
    

    Related: https://electronics.stackexchange.com/questions/148320/proper-clock-generation-for-vhdl-testbenches

    0 讨论(0)
  • 2021-01-30 18:27

    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.

    0 讨论(0)
提交回复
热议问题