simulation - codigo - simulacion vhdl
VHDL: ¿Cómo debo crear un reloj en un banco de pruebas? (4)
¿Cómo debo crear un reloj en un banco de pruebas? Ya he encontrado una respuesta, sin embargo, otros en el desbordamiento de pila han sugerido que hay alternativas o mejores formas de lograr esto:
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;
(fuente here )
Asignación de señal concurrente:
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: simulación detenida por --stop-time
gtkwave foo.ghw
Los simuladores simulan procesos y se transformarían en el proceso equivalente a su declaración de proceso. El tiempo de simulación implica el uso de espera para o después de la conducción de eventos para cláusulas de sensibilidad o listas de sensibilidad.
Mi técnica favorita:
signal clk : std_logic := ''0''; -- make sure you initialise!
...
clk <= not clk after half_period;
Por lo general, extiendo esto con una señal finished
para que pueda detener el reloj:
clk <= not clk after half_period when finished /= ''1'' else ''0'';
Alerta Gotcha: se debe tener cuidado si calcula half_period
de otra constante dividiendo por 2. El simulador tiene una configuración de "resolución de tiempo", que a menudo tiene como valor predeterminado nanosegundos ... En cuyo caso, 5 ns / 2
sale a ser 2 ns
así que terminas con un período de 4ns! Establezca el simulador en picosegundos y todo estará bien (¡hasta que necesite fracciones de un picosegundo para representar su tiempo de reloj de todos modos!)
Si se generan múltiples relojes con diferentes frecuencias, entonces la generación del reloj se puede simplificar si se llama a un procedimiento como llamada de procedimiento concurrente. El problema de la resolución de tiempo, mencionado por Martin Thompson, puede mitigarse un poco al utilizar diferentes horas altas y bajas en el procedimiento. El banco de pruebas con procedimiento para generación de reloj es:
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 resolución de tiempo se imprime en el terminal para obtener información, utilizando la última afirmación concurrente en el banco de pruebas.
Si el procedimiento clk_gen
se coloca en un paquete separado, la reutilización del banco de pruebas al banco de pruebas se convierte en una clk_gen
sencilla.
La forma de onda para los relojes se muestra en la figura siguiente.
También se puede crear un generador de reloj más avanzado en el procedimiento, que puede ajustar el período a lo largo del tiempo para que coincida con la frecuencia solicitada a pesar de la limitación por resolución de tiempo. Esto se muestra aquí:
-- 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;
Una vez más reutilizarla a través de un paquete será agradable.
Cómo usar un reloj y hacer afirmaciones.
Este ejemplo muestra cómo generar un reloj, y dar entradas y afirmar salidas para cada ciclo. Un contador simple se prueba aquí.
La idea clave es que los bloques de process
se ejecutan en paralelo, por lo que el reloj se genera en paralelo con las entradas y aserciones.
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;
El contador se vería así:
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;
Relacionados: https://electronics.stackexchange.com/questions/148320/proper-clock-generation-for-vhdl-testbenches