procedures vs. modular design?

  • Thread starter Guenter Wolpert
  • Start date
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
 
Guenter,
I tend to queue up sequences to a model/interface
by chaining up calls in a process. The key difference
I use is having a separate process for each independent
stream of data to an interface. So for example, for
a UART I have a separate process for the UART TX
and the UART RX functions. For a processor model I
worked on, I two processes, one for normal instruction
mode and one for the interrupt handler.

All the separate processes communicate using signals.

I have more details on this in my paper,
"Accelerating Verification Through Pre-Use of System-Level
Testbench Components". You can get a copy of it at:
http://www.synthworks.com/papers/

Cheers,
Jim
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jim Lewis
Director of Training mailto:Jim@SynthWorks.com
SynthWorks Design Inc. http://www.SynthWorks.com
1-503-590-4787

Expert VHDL Training for Hardware Design and Verification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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
 
When I am making a functional model of interfaces in a test bench, I
design each interface as a process. The process can be triggered by the
unit under test (UUT) if the UUT is a bus master or by a separate
process that is coordinating the testbench if the UUT is a slave.

Typically the coordinating process reads commands from a file to tell it
what to do and when. It has signals that are command or data words and
signals that trigger an interface to read the command and perform the
action. The coordinating process can wait for the command to complete,
or continue reading the command file and trigger another interface.

This was a bit complex to get working, but once I had the format worked
out it is not too hard to maintain. The more I work on it, the simpler
it gets.


Guenter Wolpert wrote:
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?
I am not clear about what you want to be independant of. If you don't
want the interface to be synchronous with the cpu clock, you just make
it asynchronous with a trigger that is controlled by whatever you want
it to be controlled by. I guess that would be simulation time, right?
You can read the current simulation time with now() and wait a given
amount of time until the time you want to start the bus cycle.

Is that what you are asking about?

--

Rick "rickman" Collins

rick.collins@XYarius.com
Ignore the reply address. To email me use the above address with the XY
removed.

Arius - A Signal Processing Solutions Company
Specializing in DSP and FPGA design URL http://www.arius.com
4 King Ave 301-682-7772 Voice
Frederick, MD 21701-3110 301-682-7666 FAX
 

Welcome to EDABoard.com

Sponsor

Back
Top