Create your preliminary test bench from your project I/O block as
Copy your I/O block into a new file '<project>_t.v'. Be sure
to create a new header for this file.
Define a term, such as 'SIMULATION', that can be used by the RTL
code (which the test bench will instantiate) for conditional
compiles related only to simulation. For example:
`define SIMULATION 1
Set a 'timescale' specification consistent with your needs and any
simulator constraints. For example:
Eliminate all input signals from the module declaration.
Convert all input signal declarations to registers (some may be
converted to wires later if they are created in subsequent
All outputs should remain outputs. All inouts should be converted
All output signals should be declared as a wire.
Simulation tools are generally immune from the requirements of
Rule 3.7 and inout signals
can become wires with multiple drivers. If these signals are
pulled up or down externally however; for best results, you should
consider making them either a 'tri0' or a tri1'. The tristates
originating from the test bench itself should still be confined,
as much as possible, to the top level module if the test bench is
partitioned; however, when using existing device models within the
test bench that have tristate signals, the combination of tristate
signals originating from these devices, the device-under-test, and
the test bench itself will occur in the top level module.
Create an 'INITIAL' block that sets all input signals to known
states. This block MUST always include
a compiler '$stop' directive, unless you are willing to wait
the hours or days until your disk completely fills up and your
computer crashes before you see any results. Some waveform
viewers, such as DAI SIGNALSCAN, also require additional
directives to control recording. Note that the stop directive
delay may be expressed as a definition, or the stop directive
itself may be expressed as a definition containing a definition. I
recommend using the former for clarity and later use. The stop
definition can reside in a separate file (with use of the 'include
statement) or, in some simulators, on the command line.
`define SIMULATION_TIME 8000000
Create "always" blocks for your principle clocks so
that they run continuously. These clocks
MUST use blocking assignments for
accurate modelling. Clock periods are best expressed with a
"`define statement" (Some advanced simulators also
support arguments within the define statement itself; enabling you
to use parameters for clock periods as an argument for the define
statement; since parameters are evaluated first in the lexical
blocks. However, for general use in projects that require
continuing support, I cannot recommend this type of construct.
Some simulators will also let you set the define statements on the
Setting your principle clock periods does not require brain surgery,
however they must be expressed in 'timescale units. We have chosen to
use a timescale of one nanosecond, with a 10 picosecond resolution.
Therefore, a 25 Mhz 50% duty cycle clock is expressed as:
`define CLK_X1_PERIOD 40
clk_x1 = #(`CLK_X1_PERIOD/2) ~clk_x1;
Asymmetric clocks may also be expressed with use of an
"always" blocks containing conditional statements. Try
to ensure that the expressions used can evaluate to an integer
value for the time unit base and resolution timespec. For example;
a sixty percent duty cycle 10 MHz clock with a 1ns and a 10ps
`define CLK_X1_PERIOD 100
if(clk_x1== 1'b0) begin
clk_x1 = #(`CLK_X1_PERIOD/10*4) ~clk_x1;
clk_x1 = #(`CLK_X1_PERIOD/10*6) ~clk_x1;
requires a "for" block or nested "for" blocks
within the "always" block. Since this mode is not needed
within our immediate project; I'll let you experiment on your own.
Cycle your master reset
(Rule 5.2) According to your
reset specifications. This is best done by setting your signal to
a reset state in the "initial" block, and the using an
"always" block, the "SIMULATION_TIME"
definition, and the additional, "POWER_ON_RESET_TIME"
For large projects, your test bench should be
partitioned as if it were a project in
itself. In other words, your test bench should model, as closely
as possible, a system environment. If you have models for external
devices, already, they should be used within the test bench. Our
project test bench will require a clock, reset, address latch,
address decoder, RAM to serve as stack and scratch pad memory, ROM
to supply instructions, simulated I/O devices, simulated DMA
devices, a source select mux to choose stimulus data from ROM, RAM,
DMA, or I/O, an interrupt generator or generators that can be
controlled by the instructions in ROM, and the DUT itself.
Test bench Block Diagram
When partitioning a test bench, signals which originate from
sub-blocks should be declared as wires in the top level module
and, within the skeleton test bench, become registers in the
sub-blocks. The initial instantiations for these particular
signals will then reside within those originating sub-blocks. Any
module that does not supply outputs directly to the top-level
module (inputs only modules or those modules whose outputs only
connect to a subsequent module) should not be instantiated until
it is actually required to provide stimulus signals for the device
under test (DUT). In our project this excludes the address latch,
ROM, RAM, and simulated I/O from the initial test bench.
Instantiate your project as a device-under-test (DUT) with inputs
and the input states of bi-directional signals coming from your
test bench. The DUT code must be incorporated into your code via
an `include statement.