VHDL - Как мне создать часы в тестовом банке?
Как мне создать часы в тестовом банке? Я уже нашел один ответ, однако другие при переполнении стека предположили, что есть альтернативные или лучшие способы достижения этого:
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;
(источник здесь)
Ответы
Ответ 1
Моя любимая техника:
signal clk : std_logic := '0'; -- make sure you initialise!
...
clk <= not clk after half_period;
Я обычно расширяю это с помощью сигнала finished
, чтобы я мог остановить часы:
clk <= not clk after half_period when finished /= '1' else '0';
Предупреждение Gotcha:
Уход следует выполнять, если вы вычисляете half_period
из другой константы путем деления на 2. Симулятор имеет настройку "временного разрешения", которая часто по умолчанию равна наносекундам... В этом случае 5 ns / 2
выступает 2 ns
> так что вы в конечном итоге с периодом 4ns! Установите симулятор на пикосекунды, и все будет хорошо (пока вам не понадобятся доли пикосекунды, чтобы представлять ваше время в любом случае!)
Ответ 2
Если несколько часов генерируются с разными частотами, то генерация часов может быть упрощена, если процедура вызывается как параллельный вызов процедуры. Вопрос о временном разрешении, упомянутый Мартином Томпсоном, может быть немного смягчен за счет использования большого и низкого времени в процедуре. Тест-стенд с процедурой генерации часов:
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;
Временное разрешение печатается на терминале для информации, используя параллельное утверждение, последнее на стенде.
Если процедура clk_gen
размещается в отдельном пакете, повторное использование с тестового стенда на тестовом стенде становится прямым.
Форма волны для часов показана на рисунке ниже.
![Waveforms for clk_166 and clk_125]()
В процедуре также может быть создан более продвинутый генератор тактовых импульсов, который может корректировать период времени, чтобы соответствовать требуемой частоте, несмотря на ограничение по временному разрешению. Это показано здесь:
-- 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;
Повторное использование через пакет будет приятным.
Ответ 3
Назначение параллельного сигнала:
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: информация: симуляция остановлена --stop-time
gtkwave foo.ghw
![enter image description here]()
Симуляторы моделируют процессы, и они будут преобразованы в эквивалентный процесс для вашего оператора процесса. Время моделирования подразумевает использование ожидания в течение или после вождения событий для предложений о секретности или списков чувствительности.
Ответ 4
Как использовать часы и делать утверждения
В этом примере показано, как генерировать часы, и давать входы и утверждать выходы для каждого цикла. Здесь протестирован простой счетчик.
Основная идея заключается в том, что блоки process
выполняются параллельно, поэтому часы генерируются параллельно с входами и утверждениями.
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;
Счетчик будет выглядеть так:
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;