|   JHDL Home Page   |   Configurable Computing Lab   |   User's Manual (TOC)   | Search

The SimulatorCallback Interface

The SimulatorCallback interface allows you to register methods which are called in response to circuit resets, updates and refreshes. This allows the creation of Java programs which follow the circuits' behavior. The SimulatorCallback interface is the preferred replacement for the Observable interface (previously a part of JHDL.)

Details

The SimulatorCallback interface is provided to assist in a variety of tasks including: (1) the writing of self-validating designs, (2) the creation of custom graphical browser elements, and (3) the creation of formatted simulation output. Any JHDL cell can implement this interface. If so, the cell must implement three methods:
  // Called each time the simulator resets
  public void simulatorReset();

  // Called each clock step after the circuit settles
  public void simulatorUpdate(int cycle, int phase);

  // Called each time the JHDL system deems a refresh of all
  // GUI elements is warranted (i.e. after a simulation run which
  // could be of multiple cycles)
  public void simulatorRefresh(int cycle, int phase);

Debug Printing With The SimulatorCallback Interface

As an example, consider the following simple design for a 4-bit (as set by the associated testbench) counter and focus in on blue section:

import byucc.jhdl.base.*;
import byucc.jhdl.Logic.*;

// Class name is TestSimCallback.  It implements the SimulatorCallback interface...
public class TestSimCallback extends Logic implements SimulatorCallback {

  public static CellInterface[] cell_interface = {
    out ( "q", "WID" ),
    param ("WID", INTEGER),
  };

  Wire q;

  // This is just the constructor as normal
  public TestSimCallback(
           Node parent,
           Wire q,
           String instanceName
         ) {
    super(parent, instanceName);
    bind("WID", q.getWidth());
    this.q = connect("q", q);

    // Build a simple counter...
    Wire s = wire(q.getWidth(), "sum");
    add_o(q, constant(q.getWidth(), 1), s, "addn");
    reg_o( s, q, "cntOut");
  }

  // The SimulatorCallback interface stuff follows


  public void simulatorReset() {
  }

  public void simulatorRefresh(int cycle, int phase) {
  }

  // Print results to screen as well
  public void simulatorUpdate(int cycle, int phase) {
    System.out.println("Cycle = " + cycle +
                       ", Phase = " + phase +
                       ": " + q.get(this));
  }
}

What is being done is that the simulatorUpdate() method is being used to print out the current state of the circuit. The simulator will call this method every step after the circuit has stabilized. The phase variable is used to count clock steps within a cycle. And the cycle variable is used to count clock cycles. See the simulator documentation for more on how the steps and cycles of the simulator relate to each other.

This same capability has been successfully used to do complex validation of stream-based DSP designs that would be extremely difficult to do by looking at waveforms. For example, consider a system where data arrives infrequently and must be converted from serial to parallel format before any other meaningful processing occurs. The simulatorUpdate() method can test the circuit's internal signals each clock cycle and print out key circuit values only on clock cycles when something of interest occurs (a conversion has completed). In addition, this output can be printed in a format that a tool such as Matlab could read in and post-process or plot. It often makes sense to also convert the bits in the circuit to their equivalent floating point representations for viewing (the JAB tools have only integer types available for display).

Creating Self-Validating Designs

It is easy to see that the usage shown above could be easily extended to have the simulatorUpdate() method actually check the correctness of the module. This would be done by comparing correct results with those generated by the circuit. The correct results could either be computed right in the simulatorUpdate() method or retrieved from a file or remotely running program.

Creating Custom Graphical Browser Elements

The simulatorRefresh() method makes it also possible to create custom browser elements such as FIFO viewers or memory viewers. For example, consider the modification to the above example in blue below:
  // The SimulatorCallback interface stuff follows
  TestFrame tf;        // Frame to hold cycle count in GUI

  public void simulatorReset() {
    // Create a testFrame if one doesn't exist yet
    if (tf != null)
      tf.dispose();
    tf = new TestFrame();
  }

  // Update information in TestFrame
  public void simulatorRefresh(int cycle, int phase) {
    tf.updateValues(q.get(this));
  }

  // Print results to screen as well
  public void simulatorUpdate(int cycle, int phase) {
    System.out.println("Cycle = " + cycle +
                       ", Step = " + step +
                       ": " + q.get(this));
  }
}

Assuming a graphical viewer class named TestFrame has been defined, this code will create the frame on reset and then update it with new values by calling its updatevalues() method each cycle. When running in single step mode in JAB, the TestFrame will then be updated with a new value each cycle.

The Complete Example

As a complete example, the following files can be used to demonstrate this capability:

tb_TestSimCallback.java The testbench of the 4-bit counter example...

TestSimulatorCallback.java A 4-bit counter...

TestFrame.java The GUI element for the 4-bit counter SimulatorCallback example...

|   JHDL Home Page   |   Configurable Computing Lab   |   Prev (Advanced Netlisting)   |   Next (Advanced Clocking)   |


JHDL 0.3.45