Aquí está mi situación: quiero simular un contador de 11 bits preestablecido formado por tres contadores binarios CD74AC161 chips. También quiero que la simulación detecte violaciones de tiempo (tiempo de configuración, tiempo de espera, etc.).
Primero, modelé el CD74AC161 de esta manera:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- entity declaration
entity cd74ac161 is
generic ( constant DATA_WIDTH : integer := 4 );
port ( clk : in std_logic; -- clock signal
clr_n : in std_logic; -- async clear signal
load_n : in std_logic; -- load/preset signal
enp : in std_logic; -- enable
ent : in std_logic;
rco : out std_logic;
d : in std_logic_vector (DATA_WIDTH - 1 downto 0);
q : out std_logic_vector (DATA_WIDTH - 1 downto 0) );
-- TODO: add detailed timing constants
constant T_PD : delay_length := 16 ns; -- Propagation delay
constant T_PW : delay_length := 4.8 ns; -- minium clock pulse width
constant T_SUA : delay_length := 4.4 ns; -- setup time for data input
constant T_SC : delay_length := 5.3 ns; -- setup/recovery time load/clear
end entity cd74ac161;
-- rtl architecture to check metastability
architecture rtl of cd74ac161 is
signal intern : std_logic_vector (DATA_WIDTH - 1 downto 0) := (others => 'X');
begin
-- clear or preset counter
intern <= (others => '0')
when clr_n = '0' else
d
when load_n = '0' else
std_logic_vector(unsigned(intern) + 1)
when (rising_edge(clk) and enp = '1' and ent = '1');
-- update RCO
rco <= '1' after T_PD when (intern = X"f" and ent = '1') else '0' after T_PD;
-- update output
q <= intern after T_PD;
-- check metastability for rising edge of clock
checkMetaStability : process is
begin
-- wait for rising edge clock
wait until rising_edge(clk);
-- check clk pulse width
assert clk'delayed'stable(T_PW)
report "CLK pulse width too short!"
severity warning;
-- check clk recovery time
assert clr_n'stable(T_SC)
report "/CLEAR changed during recovery time!"
severity warning;
-- check data setup time
assert d'stable(T_SUA)
report "Data input changed during setup time!"
severity warning;
-- check load signal setup time
assert load_n'stable(T_SC)
report "/LOAD signal changed during setup time!"
severity warning;
end process checkMetaStability;
end architecture rtl;
Y aquí está su banco de pruebas:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- entity declaration
entity cd74ac161_tb is
end entity cd74ac161_tb;
-- test bench architecture
architecture testbench of cd74ac161_tb is
signal clk : std_logic := '0'; -- clock signal
signal clr_n : std_logic := 'X'; -- async clear signal
signal load_n : std_logic := 'X'; -- load/preset signal
signal enp : std_logic := 'X'; -- enable
signal ent : std_logic := 'X';
signal rco : std_logic := 'X';
signal d : std_logic_vector (3 downto 0) := (others => 'X');
signal q : std_logic_vector (3 downto 0) := (others => 'X');
-- stop signal
signal finished : std_logic := '0';
begin
dut : entity work.cd74ac161(rtl)
port map ( clk => clk,
clr_n => clr_n,
load_n => load_n,
enp => enp,
ent => ent,
rco => rco,
d => d,
q => q );
-- clk generator
clk <= not clk after 50 ns when finished /= '1' else '0';
-- test signals
stimulus : process is
begin
-- wait for three clock cycles
clr_n <= '1';
load_n <= '1';
enp <= '0';
ent <= '0';
wait for 300 ns;
-- preload
load_n <= '0';
d <= X"a";
wait for 100 ns;
-- clear counter
load_n <= '1';
clr_n <= '0';
wait for 100 ns;
-- preload
load_n <= '0';
clr_n <= '1';
d <= X"b";
wait for 100 ns;
-- wait for 144 ns;
-- count up
load_n <= '1';
enp <= '1';
ent <= '1';
wait for 1000 ns;
-- inhibt for 500 ns
enp <= '0';
wait for 500 ns;
-- clear counter
load_n <= '1';
clr_n <= '0';
wait for 100 ns;
-- count up
load_n <= '1';
clr_n <= '1';
enp <= '1';
ent <= '1';
wait for 1000 ns;
-- WARNING: THESE SIGNALS VIOLATE METASTABILITY
-- violate clear recovery
wait until falling_edge(clk);
clr_n <= '0';
wait for 46 ns;
clr_n <= '1';
-- violate load setup
wait until falling_edge(clk);
load_n <= '0';
wait for 46 ns;
load_n <= '1';
-- violate data setup
wait until falling_edge(clk);
d <= X"e";
wait for 47 ns;
d <= X"f";
wait until falling_edge(clk);
-- --------------------------------------------
-- stop clock and wait forever
finished <= '1';
wait for 100 ns;
wait;
end process stimulus;
end architecture testbench;
Esto hasta ahora funciona como se esperaba. El proceso checkMetastability
detecta las violaciones de tiempo que las fuerzas del banco de pruebas y recibo tres advertencias por dicho proceso.
Ahora, quiero combinar tres de esos chips en un contador de 11 bits como este:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.wasp_records_pkg.all;
-- entity declaration
entity addr_counter is
generic ( constant ADDR_WIDTH : integer := 11);
port ( clk : in std_logic;
counter_in : in t_addr_counter ( d(ADDR_WIDTH -1 downto 0) );
addr_out : out std_logic_vector (ADDR_WIDTH - 1 downto 0) ); -- signals to display on LEDs
end entity addr_counter;
-- structural architecture
architecture structure of addr_counter is
-- additional/auxiliar control signals
signal load_inv : std_logic := 'X'; -- inverted load_addr signal
-- internal signals
signal rco_ct0_ct1 : std_logic := 'X'; -- RCO signal from counter0 to counter1
signal rco_ct1_ct2 : std_logic := 'X'; -- RCO signal from counter1 to counter2
begin
-- additional/auxiliar signals
-- TODO: replace with gate for correct delay
load_inv <= not counter_in.load;
-- bits 11 downto 8
addr_counter2 : entity work.cd74ac161(rtl)
port map ( clk => clk,
clr_n => counter_in.clr_n,
load_n => load_inv,
enp => rco_ct1_ct2,
ent => rco_ct1_ct2,
rco => open,
d(3) => '0',
d(2 downto 0) => counter_in.d(ADDR_WIDTH - 1 downto 8),
q(2 downto 0) => addr_out(ADDR_WIDTH - 1 downto 8) );
-- -- bits 7 downto 4
addr_counter1 : entity work.cd74ac161(rtl)
port map ( clk => clk,
clr_n => counter_in.clr_n,
load_n => load_inv,
enp => rco_ct0_ct1,
ent => rco_ct0_ct1,
rco => rco_ct1_ct2,
d => counter_in.d(7 downto 4),
q => addr_out(7 downto 4) );
-- -- bits 3 downto 0
addr_counter0 : entity work.cd74ac161(rtl)
port map ( clk => clk,
clr_n => counter_in.clr_n,
load_n => load_inv,
enp => '0',
ent => '0',
rco => rco_ct0_ct1,
d => counter_in.d(3 downto 0),
q => addr_out(3 downto 0) );
end architecture structure;
( Nota : soy consciente de que este modelo no se ajusta correctamente cuando el contador alcanza el valor máximo; aún no lo he implementado. Como es la señal para contar, falta enp
y ent
de addr_counter0
solo se conectaron a '0'
, por lo que el contador solo se puede preajustar a partir de ahora. No creo que esto se relacione con mi problema, pero quería mencionarlo de manera completa. q(3)
de addr_counter2
no está conectado, pero por alguna razón, a GHDL no le importa. Una vez más, la recuperación aún no está implementada.)
Los tres archivos funcionan hasta ahora. Aquí está el registro utilizado en el archivo de arriba:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package wasp_records_pkg is
-- input of control switches
type t_control_panel is record
deposit : std_logic;
examine : std_logic;
end record t_control_panel;
-- interface for address counter module
type t_addr_counter is record
load : std_logic; -- signal to load new address/data from input
clr_n : std_logic; -- clear the counter to zero
-- control_in : t_control_panel;
d : std_logic_vector; -- address/data input
end record t_addr_counter;
end package wasp_records_pkg;
Ahora, el banco de pruebas para addr_counter
es donde las cosas se deshacen:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.wasp_records_pkg.all;
-- entity declaration
entity addr_counter_tb is
end entity addr_counter_tb;
-- test bench architecture
architecture testbench of addr_counter_tb is
signal sys_clk, clr_n, load_addr, next_addr : std_logic := '0';
signal addr_in, addr_out : std_logic_vector (10 downto 0) := (others => 'X');
-- stop clock generator
signal finished : std_logic := '0';
begin
dut : entity work.addr_counter(structure)
port map ( clk => sys_clk,
counter_in.load => load_addr,
counter_in.clr_n => clr_n,
counter_in.d => addr_in,
addr_out => addr_out );
-- clock generator
sys_clk <= not sys_clk after 100 ns when finished /= '1' else '0';
stimulus : process is
constant CLK_CYC : delay_length := 200 ns;
begin
-- wait for 3 clock cycles
clr_n <= '1';
wait for 3 * CLK_CYC;
wait for CLK_CYC / 2;
--
-- -- load $1f0 address
-- THIS SOULD VIOLATE SETUP TIME FOR load_addr SIGNAL
addr_in <= B"001_1111_0000";
load_addr <= '1';
wait for CLK_CYC;
load_addr <= '0';
-- wait forever
finished <= '1';
wait for 3 * CLK_CYC;
wait;
end process stimulus;
end architecture testbench;
La señal load_addr
cambia al mismo tiempo que aumenta el reloj del sistema, por lo que esto debería ser una infracción de tiempo de configuración, pero mi simulador, GHDL, no imprime una advertencia como lo hace con el banco de pruebas para el CD74AC161 chip.
¿Qué me estoy perdiendo? Nuevamente, todos los archivos funcionan como se esperaba, excepto que no recibo una advertencia por violaciones de tiempo en el banco de pruebas para addr_counter
entidad.
Intenté simular esto en Vivado, pero aparentemente, Vivado no admite la simulación de registros sin restricciones .
Estoy usando GHDL 0.36-dev en Arch Linux.
EDIT : pensé que podría ser que GDHL no emita mensajes de submodelos / -componentes; así que "encajoné" el CD74AC161 y ejecuté exactamente el mismo banco de pruebas que el CD74AC161:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- entity declaration
entity blackbox is
generic ( constant DATA_WIDTH : integer := 4 );
port ( clk : in std_logic; -- clock signal
clr_n : in std_logic; -- async clear signal
load_n : in std_logic; -- load/preset signal
enp : in std_logic; -- enable
ent : in std_logic;
rco : out std_logic;
d : in std_logic_vector (DATA_WIDTH - 1 downto 0);
q : out std_logic_vector (DATA_WIDTH - 1 downto 0) );
end entity blackbox;
-- rtl architecture to check metastability
architecture rtl of blackbox is
signal load_inv : std_logic := 'X';
signal rco_ct0_ct1 : std_logic := 'X';
begin
-- additional/auxiliar signals
-- TODO: replace with gate for correct delay
load_inv <= not load_n;
-- -- bits 3 downto 0
addr_counter0 : entity work.cd74ac161(rtl)
port map ( clk => clk,
clr_n => clr_n,
load_n => load_inv,
enp => '0',
ent => '0',
rco => rco_ct0_ct1,
d => d(3 downto 0),
q => q(3 downto 0) );
end architecture rtl;
Esto también imprime correctamente las advertencias de GHDL, como espero / deseo. Entonces no puede ser eso.