G
Guenter Wolpert
Guest
Hello,
What is the best way to trigger an action in an external unit?
Background is I want to trigger bus cycles in a simulation model
from within a testbench.
First tests were implemented with the bus cycle implemented in a
procedure that was defined in the declaration part of the main testbench
process:
entity testbench is
end entity testbench;
architecture behavioral of testbench is
begin -- architecture behavioral
tb: process
procedure BusWrite (
constant address : in std_logic_vector(12 downto 0);
constant data : in std_logic_vector(7 downto 0)) is
begin
CPU_A <= address;
CPU_D <= data after 10ns,
(others <= 'Z') after 20ns;
--and so on...
end procedure BusWrite;
begin
BusWrite(X"000", x"ab");
--and so on...
end process tb;
end architecture behavioral;
However, during the bus cycles I want to be able to do other
operations, so I don't want to wait until the bus cycle is over.
Further, the simulation model should be in a separate unit or
preferrably in a package, so that is independent of the testbench
and can be re-used in other projects.
Second attempt:
For a synchronous interface I built a framework that consists of
- a trigger procedure that simply sets a trigger variable and
locks against other acesses so that they don't overlap
- a polling process that is implemented in a separate entity.
The polling process is sensitive to the interface clock and checks
the trigger variable for each clock edge. If the trigger condition
is met, a signal is set on which the simulation model it self is
triggered.
- The simulation model preforms the bus cycle autonomously, incuding
timing and data checks.
The code below should show the concept:
package CpuSimModel is
procedure BusWrite (
constant address : in std_logic_vector(12 downto 0);
constant data : in std_logic_vector(7 downto 0));
signal trig_write : std_logic; --trigger sim model process
end package CpuSimModel;
package body CpuSimulator is
procedure BusWrite (
constant address : in std_logic_vector(12 downto 0);
constant data : in std_logic_vector(7 downto 0)) is
begin
trig_wr := '1'; --set trigger variable
wait until wr_busy = '1'; --wait until acknowledged
trig wr := '0'; --remove trigger
end procedure BusWrite;
end package body CpuSimulator;
entity CpuSimModel is
port(
CPU_A : std_logic_vector(11 downto 0);
CPU_D : std_logic_vector(7 downto 0);
CPU_CLK : std_logic);
end entity CpuSimModel;
architecture behavioral of CpuSimModel is
begin -- architecture behavioral
PollTrigger: process(CPU_CLK) is
begin
wait until rising_edge(CPU_CLK);
if trig_wr = '1' and wr_busy = '0' then trig_write <= '1'; end if;
end process PollTrigger;
WriteAccess: process is
begin
CPU_A <= (others => 'Z');
CPU_D <= (others => 'Z');
wait until trig_write = '1';
CPU_A <= addr after 10 ns, (others => 'Z') after 20 ns;
CPU_D <= data after 10 ns, (others => 'Z') after 20 ns;
end process Write Access;
end architecture behavioral;
This scheme works fine for a synchronous interface and the overhead
by the polling process should not be too high. However, if I want
to trigger the access independent of a clock there seems no way to
implement this independent of testbench testbench code.
Is there any way to define a procedure in a package that sets a signal?
Or is there any other good way to trigger actions in external design
units without implementing trigger procedures locally in each testbench?
regards
Guenter
--
guenter.wolpert@t-online.de
What is the best way to trigger an action in an external unit?
Background is I want to trigger bus cycles in a simulation model
from within a testbench.
First tests were implemented with the bus cycle implemented in a
procedure that was defined in the declaration part of the main testbench
process:
entity testbench is
end entity testbench;
architecture behavioral of testbench is
begin -- architecture behavioral
tb: process
procedure BusWrite (
constant address : in std_logic_vector(12 downto 0);
constant data : in std_logic_vector(7 downto 0)) is
begin
CPU_A <= address;
CPU_D <= data after 10ns,
(others <= 'Z') after 20ns;
--and so on...
end procedure BusWrite;
begin
BusWrite(X"000", x"ab");
--and so on...
end process tb;
end architecture behavioral;
However, during the bus cycles I want to be able to do other
operations, so I don't want to wait until the bus cycle is over.
Further, the simulation model should be in a separate unit or
preferrably in a package, so that is independent of the testbench
and can be re-used in other projects.
Second attempt:
For a synchronous interface I built a framework that consists of
- a trigger procedure that simply sets a trigger variable and
locks against other acesses so that they don't overlap
- a polling process that is implemented in a separate entity.
The polling process is sensitive to the interface clock and checks
the trigger variable for each clock edge. If the trigger condition
is met, a signal is set on which the simulation model it self is
triggered.
- The simulation model preforms the bus cycle autonomously, incuding
timing and data checks.
The code below should show the concept:
package CpuSimModel is
procedure BusWrite (
constant address : in std_logic_vector(12 downto 0);
constant data : in std_logic_vector(7 downto 0));
signal trig_write : std_logic; --trigger sim model process
end package CpuSimModel;
package body CpuSimulator is
procedure BusWrite (
constant address : in std_logic_vector(12 downto 0);
constant data : in std_logic_vector(7 downto 0)) is
begin
trig_wr := '1'; --set trigger variable
wait until wr_busy = '1'; --wait until acknowledged
trig wr := '0'; --remove trigger
end procedure BusWrite;
end package body CpuSimulator;
entity CpuSimModel is
port(
CPU_A : std_logic_vector(11 downto 0);
CPU_D : std_logic_vector(7 downto 0);
CPU_CLK : std_logic);
end entity CpuSimModel;
architecture behavioral of CpuSimModel is
begin -- architecture behavioral
PollTrigger: process(CPU_CLK) is
begin
wait until rising_edge(CPU_CLK);
if trig_wr = '1' and wr_busy = '0' then trig_write <= '1'; end if;
end process PollTrigger;
WriteAccess: process is
begin
CPU_A <= (others => 'Z');
CPU_D <= (others => 'Z');
wait until trig_write = '1';
CPU_A <= addr after 10 ns, (others => 'Z') after 20 ns;
CPU_D <= data after 10 ns, (others => 'Z') after 20 ns;
end process Write Access;
end architecture behavioral;
This scheme works fine for a synchronous interface and the overhead
by the polling process should not be too high. However, if I want
to trigger the access independent of a clock there seems no way to
implement this independent of testbench testbench code.
Is there any way to define a procedure in a package that sets a signal?
Or is there any other good way to trigger actions in external design
units without implementing trigger procedures locally in each testbench?
regards
Guenter
--
guenter.wolpert@t-online.de