![]() |
|
|
import byucc.jhdl.base.*; import byucc.jhdl.Logic.*; import byucc.jhdl.Xilinx.Virtex.*; public class tb_FullAdder extends Logic implements TestBench {
In addition to importing both base and Logic, you must import the library for the chosen technology for the design. In addition, your testbench class should extends Logic and implements TestBench.
static HWSystem hw; public static void main(String argv[]) { hw = new HWSystem(); tb_FullAdder tb = new tb_FullAdder(hw); new cvt( tb ); }
Because your test bench is a stand-alone Java program it must have a main routine. A number of things are going on in this main routine:
private Wire a, b, sum, cout;
Next, some wires to be used to connect the test bench to the FullAdder which will be created below are declared.
public tb_FullAdder(Node parent) { super(parent); setDefaultTechMapper(new VirtexTechMapper(true)); a = wire(1, "a"); b = wire(1, "b"); sum = wire(1, "sum"); cout = wire(1, "cout"); new FullAdder(this, a, b, gnd(), sum, cout); }
This next section is the constructor for the test bench. After calling super() like all Logic cells must do, it sets the technology to be Virtex. More precisely, it creates a new Virtex techmapper and then tells JHDL that it should be used for all techmapping as the circuit is built. It then creates the wires to connect the test bench to the FullAdder and then creates the FullAdder. Note that since no carry in is desired, a gnd() call is used to tie that input to a low voltage.
public void reset() { a.put(this, 0); b.put(this, 0); cnt = 0; } int cnt = 0; public void clock() { a.put(this, cnt & 0x1); b.put(this, (cnt>>1) & 0x1); cnt++; } }
All test benches must provide stimulus to the circuit under test. In this case, the a and b wires must be driven to exercise the adder. This example shows the simplest way to do this. It is done by creating reset() and clock() methods. The reset method is called at the start of each simulation run. All it needs to do here is to drive 0's into the circuit. In addition, it resets the clock counter for use later.
The clock routine gets called after every single clock phase. What the code here does is count clock phases and use that value to derive values to drive into the a and b inputs to the FullAdder circuit. As you simulate you will see that this cycles through all 4 input combinations very nicely. The above file can be found as tb_FullAdder.java and the FullAdder as FullAdder.java if you want to experiment with them.
Further, it would be a simple matter to augment the code to do correctness checking like this:
public void clock() { int res = a.get(this) + b.get(this); if ((res & 0x1) != sum.get(this) || ((res>>1) & 0x1) != cout.get(this)) System.err.println("Addition error: " + a.get(this) + " " + b.get(this) + " " + sum.get(this) + " " + cout.get(this)); a.put(this, cnt & 0x1); b.put(this, (cnt>>1) & 0x1); cnt++; } }
Since the clock routine is called only after the circuit has stabilized in simulation, it is a simple matter to get the values off the wires of interest and determine whether they were correct. This is done before putting the new values for the next cycle onto them.
As with stimulus above, it would be a simple matter to use arrays or files to contain the correct responses to the stimulus provided.
int cnt = 0; public void clock() { boolean newcycle; if (hw.getCurrentStepCount() == 0) indx++; someWire.put(this, vals[indx]); cnt++; }
This works by asking the HWSystem the current step count and changing an index into the stimulus data each time a new cycle begins.
Step 1 is easy:
public class tb_NBitAdder extends Logic implements ProgrammaticTestBench {
Here is an example of step 2:
private long a_val, b_val, sum_val; public void execute() { final long a_iterations = 1<<adder_width; final long b_iterations = 1<<adder_width; for (long i=0;i < a_iterations;i++) { for (long j=0;j < b_iterations;j++) { a_val = i; b_val = j; getSystem().cycle(1); // Step system clock 1 cycle sum_val = sum.getL(this); long correct = a_val + b_val; if (sum_val != correct) { System.out.println("NBitAdder failed miserably!"); System.out.println("a = " + a_val); System.out.println("b = " + b_val); System.out.println("sum = " + sum_val); System.out.println("correct = " + correct); return; } } } System.out.println("NBitAdder passed with flying colors."); }
The execute routine here uses doubly nested loops to exhaustively test the circuit by computing values for a and b based on the loop counters. It then asks the system to step the clock by 1 cycle after which it determines whether the sum was computed by the circuit correctly.
Another point of interest is that long integers are used for everything here. This is because above 32 bits wide, wire values won't fit into a conventional int. This test bench is designed to allow for the testing of NBitAdders wider than 32 bits wide. Thus, the sum.getL() is needed - getL will get the value of a wire more than 32 bits wide.
Note that above, the new values to be driven into the circuit are only computed. The reset and clock routines still must drive those values onto the wires. That is step 3. Here is the code:
public void reset() { a.putL(this, 0); b.putL(this, 0); } public void clock() { a.putL(this, a_val); b.putL(this, b_val); }
Since the values to be driven onto the wires are longs, putL calls are used.
In summary, ProgrammaticTestBench makes it much easier to write pattern generators such as the exhaustive tester above. A complete ProgrammaticTestBench for the NBitAdder example is available as tb_NBitAdder.java. This file is also interesting because it uses command line arguments to let the user tell whether a cvt GUI environment is desired. It also uses command line arguments to control whether a netlist should be generated (ignore that part of the code for now - it is described later in the section on advanced netlisting).
| JHDL Home Page | Configurable Computing Lab | Prev (Netlisting) | Next (Stimulators) | |
JHDL 0.3.45
Copyright (c) 1998-2003 Brigham Young University. All rights reserved.
Last updated on 11 May 2006