| Lab Home Page
|
The documentation in this directory deals with the various tools used for circuit visualization
This page contains information on how to use the interactive stimulator.
| PAGE SUMMARY |
|---|
The interactive stimulator is a tool that can be used with your
test bench to interactively (as opposed to programmatically)
stimulate the top-level port wires of your design. Without an
interactive stimulator, all of the values on the top-level wires
must be done with calls to the put method of the
Wire class within the clock method. This
programmatic approach is usually a more static approach.
Programmatic wire stimulation has advantages and disadvantages.
On the pro side, programmatic wire stimulation allows for precise
and consistent design simulation. On the con side, it hinders
experimentation since the test bench usually must be recompiled
after every little modification to the sequence of puts.
With the interactive stimulator you don't have to recompile your test bench in order alter the sequence of puts on port wires. The puts can be determined interactively during simulation. And the interactive stimulator also still maintains the precise and consistent advantages of statically programmed stimulation.
This tutorial describes how to the use the interactive stimulator in JHDL. There are two simple steps to using the interactive stimulator:
To set up your JHDL TestBench to use the interactive stimulator, you need to import the Stimulator class. Just include the following at the top of your TestBench file:
import byucc.jhdl.apps.Stimulator.Stimulator;
In addition, if you are using the Properties class to initialize put schedules, you may need to import that, or some other precursor class, as well:
import java.util.Properties;
Use the new operator in
Java to instance a Stimulator object.
Stimulator is a subclass of Logic and will become a child cell of
your testbench along with the design encapsulated by the
testbench. There are several constructors available to instance
the Stimulator. All constructors require a parent Cell (your
TestBench). Nearly all constructors allow you to add your wires
to the stimulator in the constructor itself. Several constructors
also allow you to designate a Properties object or Properties
file. Properties can then be used to associate a wire (indicated
by its name) with a schedule sequence. See the Java API
documentation for information on using the Properties class or
creating a file or input stream that can be used to generate a
Properties object.
Wires can be connected to a Stimulator either in the constructor,
or separately in calls the addWire method. All wires
connected to the Stimulator should be children of the Stimulator's
parent. When
adding wires to the Stimulator's set of wires, you can also
specify the put schedules for those wires.
The following are some examples of creating a Stimulator object and associating wires with it.
This is a basic example that creates a Stimulator and adds two wires to it. The first wire will just have the default schedule of 0. The second wire will have a custom schedule of 0 1 2 2 1. The custom schedule will put the values on the wire, one at a time, one on each clock cycle.
/* Declare the wires */
Wire portAIn;
Wire portBIn;
... /* Bind the wires to the ports */
/* Make a Stimulator with this TestBench as parent */
Stimulator stimulator = new Stimulator( this );
/* Add portAIn with default put schedule: "0" */
stimulator.addWire( portAIn );
/* Add portBIn with custom put schedule: 0, 1, 2, 2, 1, 0, 1, 2, 2, 1, etc. */
stimulator.addWire( portBIn, "0 1 2 2 1" );
This example shows the contents of a Properties file and the code
from a TestBench that instantiates a Stimulator for some wires.
The syntax of java.util.Properties file can be found in the
description of the load method of that class.
schedules.props file contents:
# This file contains the schedules for the inputA and inputB wires of my TestBench
inputA = 0 1 1 1 0 1 0 15
inputB : 10 9 8 7 5 4 3 2 1 15
Portions of the TestBench class:
/* Declare the wires */
Wire inputA;
Wire inputB;
Wire inputC;
... /* Bind the wires to the ports */
/* Put the wires in a Collection object, such as a HashSet */
java.util.HashSet stimulatorWires = new java.util.HashSet(3);
stimulatorWires.add( inputA );
stimulatorWires.add( inputB );
/* Make a Stimulator with this TestBench as parent */
/* The wires are passed to it in a Collection object */
/* The schedules will be loaded from the Properties file given */
Stimulator stimulator = new Stimulator( this, stimulatorWires, "schedules.props" );
/* We can still add more wires but we have to specify the
schedule if we want a non-default schedule (i.e. the schedules
in the Properties file are no longer available) */
stimulator.addWire( inputC, "5 8 1 0 15" );
You can also create multiple Stimulators. This is especially
handy if you want to have wires on different clock schedules.
To associate a Stimulator with a different clock schedule, simply
set the default clock before instantiating the stimulator:
/* Declare the wires */
Wire portAIn;
Wire portBIn;
Wire clockA, clockB;
... /* Bind the wires to the ports and create the clocks */
/* Make a Stimulator with this TestBench as parent */
setDefaultClock( clockA );
Stimulator stimulator = new Stimulator( this );
/* Add portAIn with default put schedule: "0" */
stimulator.addWire( portAIn );
/* Make a different Stimulator on clock schedule B */
setDefaultClock( clockB );
stimulator = new Stimulator( this );
/* Add portBIn with custom put schedule: 0, 1, 2, 2, 1, 0, 1, 2, 2, 1, etc. */
stimulator.addWire( portBIn, "0 1 2 2 1" );
In some situations, it is important to excercise great care with setting and using clocks with Stimulators. See the section on using clocks with Stimulators for more information.
For information on special considerations when using Stimulators on tri-state buses, see the tri-state section.
NOTE: When creating your wires, it may be useful to give them a specific name so that you will know for sure how to access them through the command-line interface.
It is important to note that Stimulators are themselves merely JHDL circuit elements; in fact Stimulators are just instances of the class byucc.jhdl.Logic.Logic. As a Logic, Stimulators are clockable. Therefore, even if your entire design is purely combinational, if you have Stimulators driving your design, you will have a clock in the system. By default, the Stimulators simply use the current default clock. Stimulators are by default rising-edge triggered; currently, there is no support for making a Stimulator falling-edge triggered.
Sometimes a user design uses explicit clocks or needs to have a unique clock schedule (the pattern, including duty cycle, etc. of a clock wire's driver). If your design uses explicit clocks or if it sets the default clock, then you must also make sure that you set up the clock for the Stimulator. JHDL does not allow designs to mix explicit with implicit clocks. The Stimulator just uses the default clock available to it at build time; if the clock hasn't been set prior to building the Stimulator, it uses the global default clock made available by the HWSystem. But if your design uses explicit clocks, you must make sure that you set the default clock before you instance a Stimulator. (Just call the setDefaultClock method of Logic, giving it a wire driven by a clock driver.) Make sure that all of your clocks have the same length of schedule.
Stimulators should typically only be clocked by wires that are the outputs of a clock driver. (i.e. avoid using gated clocks and other unusual clock wires for Stimulators.) Also, to avoid problems with clocking Stimulators, it is a good idea to build the Stimulators last. By making sure that the TestBench builds your design and any other circuitry before instancing a Stimulator, you help to ensure that the Stimulator will be properly clocked.
Summary of using clocks with Stimulators:
In order to be allowed to drive a tri-state bus wire, a JHDL cell must implement the TriStateDriver interface. When a tri-state bus wire is added to a Stimulator, a new Stimulator that implements the TriStateDriver interface is created to handle that wire. That one Stimulator is then responsible for only that one wire. In order to ensure that the Stimulator recognizes that a wire is a tri-state bus, it is essential that the wire is connected to another TriStateDriver, a pullup, or a pulldown before it is added to a Stimulator. Alternatively, you can instance a TriStateStimulator directly. (The constructor for a TriStateStimulator takes a parent Node, the Wire, and a String for the schedule, in that order.)
If at any time a tri-state bus Wire being driven by a Stimulator is to let the wire float or allow some other device to drive it, the schedule for that wire at that point should issue a "Z". That is, if you use the standard ValueForcers to generate the value sequence for the wire, indicate a high impedance output with "Z" in your schedule string. For custom ValueForcers (see the section on function generators), make sure the underlying ValueProvider properly implements the isDrivingOutput() method appropriately. See the API documentation for more information.
If the put scheudules placed on the wires in the TestBench are what you want to simulate in JHDL, you do not need to do anything else with the interactive stimulator. You may simply execute the simulation normally.
If yo do want to interactively update the schedules on the wires, you can use the JHDL command-line interface (CLI). The interactive stimulator registers two commands in the JHDL CLI. It is through these commands that you interact with the stimulator.
PUTLIST COMMAND. The putlist command is used to display the names of the wires that are currently registered with any Stimulator object.
PUT COMMAND. The put command is used to create or update a custom schedule for a given wire. The syntax is:
put wire_name value|schedule
A value is a decimal, hexadecimal, octal, or binary value
(prefix 0x, 0o, or 0b for hexadecimal, octal, or binary).
A value may also be Z. (Useful for inout ports.)
A schedule is a space-separated list of values. A
schedule may also be the name of a custom function generator
object along with appropriate arguments for it. See
the advanced section for more information
on custom schedules.
The following are examples of calls to the put
command:
put myWire1 0b110myWire1 with the value 6 on every clock
cycle.put myWire2 0b110 0x10 8 0o12myWire2 with the sequence of values 6, 16, 8,
and 10. On the first clock cycle, the stimulator will drive the
wire with a 6, on the second, with a 16, on the third, an 8, on
the fourth, a 10. Then the process repeats so that on the fifth
cycle, the stimulator will drive the wire with a 6, and so
forth.
The following files contain a full JHDL example that uses the
interactive stimulator. With the JHDL classes or JHDL.jar in your
classpath, compile AndNReg and tbAndNRegStimulator. Then execute
tbAndNRegStimulator with the -gui option:
javac tbAndNReg3BitStimulator.java AndNReg3Bit.java
java tbAndNReg3BitStimulator -gui
| AndNReg3Bit.java | This is the design that is wrapped by the TestBench |
| tbAndNReg3BitStimulator.java | The TestBench for this example. This is where the Stimulator is created. |
| sched.props | This is a Properties file that is used by this example |
The following files contain a another example that uses the interactive stimulator. With the JHDL classes or JHDL.jar in your classpath, compile CustomClockCell and tbCustomClockCell. Then execute tbCustomClockCell:
javac tbCustomClockCell.java CustomClockCell.java
java tbCustomClockCell
| CustomClockCell.java | This is the design that is wrapped by the TestBench |
| tbCustomClockCell.java | The TestBench for this example. This is where the Stimulator is created. This TestBench uses custom, user-defined clocks and a tri-state Stimulator. This TestBench is not as full-featured as tbAndNReg3BitStimulator; it is meant to show how to deal with user-defined clocks and tri-state wires. |
The Stimulator also allows you to create your own custom function generator to provide the values to put on an input wire. To create a custom function generator, simply create a class that implements one of the sub-interfaces of byucc.jhdl.apps.Stimulator.ValueProvider. Then, to apply your function generator to a wire during simulation, use the put command.
The put command has the following syntax:
put value|schedule|ValueProvider_arguments
help put for more information on the syntax of
the arguments to create a ValueProvider. Use this same syntax when
creating a ValueProvider for the Wire from withing the TestBench.
The example at the end of this section demonstrates how to do this.
There are four different sub-interfaces of the ValueProvider interface. These interfaces, which are also in the byucc.jhdl.apps.Stimulator package, are BooleanValueProvider, IntegerValueProvider, LongValueProvider, and BVValueProvider. These interfaces are designed so that, if necessary, you can implement all four, or any combination. Having four different interfaces is desirable to allow you to select the interface that most closely matches your design. For one-bit wires, the BooleanValueProvider interface is sufficient; but for wires up to 32 bits wide, you should probably use the IntegerValueProvider. Likewise, LongValueProvider is adequate for wire widths up to 64 bits and BVValueProvider can be used for arbitrary-width wires.
Each ValueProvider interface requires you to implement two versions
of the method public String
getForceSchedule(...). One
getForceSchedule method takes an int to
be used as a radix. The other takes a String to be
used as a BV format string. Use these methods to output a
String that will represent the series of values or the
function that your ValueProvider generates.
Each ValueProvider interface also requires you to implement the
methods public [return_type]
getNext[ReturnType]Value() and public [return_type]
getReset[ReturType]Value(), where return_type is one
of boolean, int, long, or BV, and ReturnType is Boolean, Integer,
Long, or BV respectively. Use the getNext[ReturnType]Value method
to provide successive values of your function for each clock
cycle. Use the getReset[ReturnType]Value method to provide the
value that the wire should have at system reset.
Once you have written and compiled your function generator, you can
apply it to your wire using the put command from the command line
in the circuit visualization window of JHDL. The put command has
been overloaded to allow you to either put a single value or series
of values (as described above) or to
instance a ValueProvider and associate it with a wire. Run the
command help put to view the
syntax and other helpful information. The syntax of this use of
the put command allows you to specify which (public) constructor of
your ValueProvider class (if it happens to have more than one
constructor) and which of the ValueProvider interfaces you wish it
to act as during simulation (if it happens to implement more than
one). You may also provide arguments to the constructor of your
ValueProvider to set up any parameters it may have.
The following files contain a full JHDL example that uses the
interactive stimulator and a custom function generator. With the
JHDL classes or JHDL.jar in your classpath, compile AndNRegNBit,
tbAndNReg32Function, and Cosine. Then execute tbAndNReg32Function
with the -gui option:
javac tbAndNReg32Function.java AndNRegNBit.java Cosine.java
java tbAndNReg32Function -gui
| AndNRegNBit.java | This is the design that is wrapped by the TestBench. |
| tbAndNReg32Function.java | The TestBench for this example. This is where the Stimulator is created and a custom function generator is associated with the wires. |
| Cosine.java | A custom function generator that will output a cosine function. |
| CustomCosineView.java | A custom viewer to watch wires with cosine values on them. |
| Lab Home Page |
Copyright (c) 1998-2002 Brigham Young University. All rights reserved.
Reproduction in whole or in part in any form or medium without express
permission of Brigham Young University is prohibited.
Documentation Revision: $Date: 2002/04/05 $