The TOFU Circuit Simulator

Software

Download

Grab the source and jar here:

tofu20070128.zip

Usage

Tofu is executed from the command line using Java 5 or above:

java -jar tofu.jar
Args: [tofu file] [optional config file]

Or like this:

java -cp tofu.jar tofu.Main
Args: [tofu file] [optional config file]

To illustrate, create a file called test.tofu with the following lines:

# AND Gate
def and i1 i0 o :
  r i0 z 1 0
  r i1 o z 0

run 10 1 and 1 1 ?

Run it as follows:

java -jar tofu.jar test.tofu
Build complete: 2 components, 4 nodes.
1: 0
2: 1

Extending

Itís not always possible or practical to construct all components out of relays. Luckily, Tofu was designed to be extendable. It allows you to add new components created in Java.

The Tofu simulation acts upon components linked together by nodes. Each iteration of the simulation consists of 2 phases. In the first phase, the power voltage propagates from the power node through the electronic components successively energizing nodes along the way. If a circuit pathway exists between the power node and some other node, that node is energized and is said to be ďreachableĒ from the power node. Unreachable nodes are left floating; they are not connected to power or ground. At the end of the first phase, reachable nodes carry the power voltage (high, H) and unreachable nodes are floating (Z). In the second phase, the internal states of the electronic components change in response to nodes they are connected to. In the case of a relay, that means the lever may flip position in response to the coil node value.

Nodes are represented by the Node class:

package tofu;

import java.util.*;

public class Node {
  
  private Voltage voltage = Voltage.Z;
  private Set<Component> components = new HashSet<Component>();
   
  public Voltage getVoltage() {
    return voltage;
  }
  
  public void setVoltage(Voltage voltage) {
    this.voltage = voltage;
  }
  
  public Set<Component> getComponents() {
    return components;
  }
}

The voltage carried by the node is represented by the Voltage enum:

package tofu;

public enum Voltage { H, Z }

Each node is connected to a set of components each of which extends the abstract class Component:

package tofu;

import java.util.*;

public abstract class Component {

  public abstract void init();
  public abstract void flow();
  public abstract void update();
  
  public String getInitParam() {
    // ...
  }
  
  protected Node getNode(String name) {
    // ...
  }
  
  protected void energizeNode(Node node) {
    // ...
  }
  
  // ...
  
}

Component is the class you need to extend to define custom components. Inside the init() method, you should capture references to the input and output nodes that the component connects to. Each of those nodes has a name that you define in a configuration file, which will be discussed below. You acquire those references via getNode(). In the first phase of each simulation iteration, the power voltage propagates through the components. Every time it reaches a node connected to your component, the flow() method is invoked. Itís up to flow() to propagate the signal through the component. To energize an output node, call energizeNode(). In the second phase of each simulation iteration, update() is called on all components to modify their internal states. Inside of update(), you inspect the input nodes and change state according.

As an example, hereís an alternative way to define an AND gate:

package tofu.test;

import tofu.*;

public class AndGate extends Component {
  
  private Node input1;
  private Node input2;
  private Node output;
  private boolean and;
  
  public void init() {
    input1 = getNode("input1");
    input2 = getNode("input2");
    output = getNode("output");
  }
  
  public void update() {
    and = input1.getVoltage() == Voltage.H && input2.getVoltage() == Voltage.H;
  }
  
  public void flow() {
    if (and) {
      energizeNode(output);
    }
  }
}

The AND gate holds references to 2 input nodes and an output node. It acquired those references inside of init(). When update() is called, it internally changes state based off the input voltages. The next time flow() is called, it energizes the output node based on its state.

To use this class, you need to define a configuration file containing this line:

tofu.test.AndGate and input1 input2 output

Each line of the configuration file specifies the class name, the name of the component as it will be used in the Tofu file followed by the components parameter list. There is no real distinction between input nodes and output nodes.

To test it, letís define a single line Tofu file, test2.tofu:

run 10 1 and 1 1 ?

And finally, letís run it:

java -cp tofu.jar;test2.jar tofu.Main test2.tofu test2.config
Build complete: 1 components, 1 nodes.
1: 0
2: 1

To execute it, the AndGate class must be in the classpath. In the example above, the class file was added to a jar named test2.jar.

Sometimes itís useful to give individual components names. For example, consider a simple light bulb:

package tofu.test;

import tofu.*;

public class Bulb extends Component {

  private Node inputNode;
  private Voltage lastVoltage = null;
  private String name;
  
  public void init() {
    inputNode = getNode("input");
    name = getInitParam();
  }

  public void update() {
    if (inputNode.getVoltage() != lastVoltage) {
      System.out.printf("%s: %s%n", name, 
          inputNode.getVoltage() == Voltage.H ? "1" : "0");
    }
    lastVoltage = inputNode.getVoltage();
  }

  public void flow() {
  }
}

The bulbís internal state manifests itself as lit or unlit. In the case of this simulated bulb, it prints out 0 or 1 only in the event that it changes state (see the update() method). Inside of init(), note the call to getInitParam(). The initialization parameter is a string wrapped in quotes and passed to the component in the Tofu file as the first parameter. To indicate that a component has an initialization parameter, you add <init> to the associated configuration file line like this:

tofu.test.Bulb bulb <init> input

Inside the Tofu file, letís create a bulb called, ďMy BulbĒ, and make the input node the clock node:

run 10 1 bulb "My Bulb" c

Now, letís run it:

java -cp tofu.jar;test3.jar tofu.Main test3.tofu test3.config
Build complete: 1 components, 0 nodes.
My Bulb: 0
My Bulb: 1
My Bulb: 0
My Bulb: 1
My Bulb: 0
My Bulb: 1
My Bulb: 0
My Bulb: 1
My Bulb: 0
My Bulb: 1

As you can see, we used the clock to make a blinking light.


Updated: January 28, 2007. Copyright 2007 Michael Birken. All rights reserved.