Simple Counter

Often we want components that aren't exclusively combinational in nature - that is, we want the component to have some memory. There is an important subtlety in defining such components: You can't have the component itself store the state, because an individual component can appear many times in the same circuit. It can't appear directly within a circuit multiple times, but it can appear multiple times if it appears in a subcircuit that is used several times.

The solution is to create a new class for representing the object's current state, and to associate instances of this with the component through the parent circuit's state. In this example, which implements an edge-triggered 8-bit counter, we define a CounterState class to accomplish this, in addition to the Component and ComponentFactory implementations that the previous examples have illustrated. The CounterState object remembers both the counter's current value, as well as the last clock input seen (to detect rising edges).

SimpleCounter

package com.cburch.incr;

import com.cburch.logisim.circuit.CircuitState;
import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.comp.ComponentFactory;
import com.cburch.logisim.comp.EndData;
import com.cburch.logisim.comp.ManagedComponent;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.util.GraphicsUtil;
import com.cburch.logisim.util.StringUtil;

/** Represents a simple 8-bit counter. This example illustrates how a
 * component can maintain its own internal state. */
class SimpleCounter extends ManagedComponent {
    /** The width of input and output. */
    private static final BitWidth BIT_WIDTH = BitWidth.create(8);
    
    // Note what's not here: We don't have any instance variables referring
    // to the counter's state. Using instance variables to refer to state
    // would be a major bug, because this component may appear in a circuit
    // that is used several times as a subcircuit to another circuit. Thus,
    // this single component would actually appear many times in the overall
    // circuit. Any instance variables storing state would lead to weird
    // interactions among the states. Instead, we need to store all state
    // information in an object stashed into a CircuitState.
    
    SimpleCounter(Location loc, AttributeSet attrs) {
        super(loc, attrs, 2);
        setEnd(0, loc.translate(-30, 0), BitWidth.ONE, EndData.INPUT_ONLY);
        setEnd(1, loc,                   BIT_WIDTH,    EndData.OUTPUT_ONLY);
    }
    
    public ComponentFactory getFactory() {
        return SimpleCounterFactory.instance;
    }

    public void propagate(CircuitState circuitState) {
        // Here I retrieve the state associated with this component via
        // a helper method. In this case, the state is in a CounterState
        // object.
        CounterState state = getCounterState(circuitState);

        Value clk = circuitState.getValue(getEndLocation(0));
        if(state.getLastClock() == null ||
                (state.getLastClock() == Value.FALSE && clk == Value.TRUE)) {
            // Either the state was just created, or else we're on a rising edge
            // for the clock input; in either case, increment the counter.
            Value newValue = Value.createKnown(BIT_WIDTH,
                    state.getValue().toIntValue() + 1);
            circuitState.setValue(getEndLocation(1), newValue, this, 9);
            state.setValue(newValue);
        }
        state.setLastClock(clk);
        
        // (You might be tempted to determine the counter's current value
        // via circuitState.getValue(getEndLocation(1)). This is erroneous,
        // though, because another component may be pushing a value onto
        // the same wire, which could lead to conflicts that don't really
        // represent the value the counter is emitting.)
    }

    public void draw(ComponentDrawContext context) {
        context.drawRectangle(this);
        context.drawClock(this, 0, Direction.EAST);
        context.drawPin(this, 1);
        
        // I'd like to display the current counter value centered within the
        // rectangle. However, if the context says not to show state (as
        // when generating printer output), then I shouldn't do this.
        if(context.getShowState()) {
            CounterState state = getCounterState(context.getCircuitState());
            Bounds bds = getBounds();
            GraphicsUtil.drawCenteredText(context.getGraphics(),
                    StringUtil.toHexString(BIT_WIDTH.getWidth(), state.getValue().toIntValue()),
                    bds.getX() + bds.getWidth() / 2,
                    bds.getY() + bds.getHeight() / 2);
        }
    }
    
    /** Retrieves the state associated with this counter in the circuit state,
     * generating the state if necessary.
     */
    protected CounterState getCounterState(CircuitState circuitState) {
        CounterState state = (CounterState) circuitState.getData(this);
        if(state == null) {
            // If it doesn't yet exist, then we'll set it up with our default
            // values and put it into the circuit state so it can be retrieved
            // in future propagations.
            state = new CounterState(null, Value.createKnown(BIT_WIDTH, -1));
            circuitState.setData(this, state);
        }
        return state;
    }

}

CounterState

package com.cburch.incr;

import com.cburch.logisim.comp.ComponentState;
import com.cburch.logisim.data.Value;

/** Represents the state of a counter. */
class CounterState implements ComponentState, Cloneable {
    /** The last clock input value observed. */
    private Value lastClock;
    
    /** The current value emitted by the counter. */
    private Value value;

    /** Constructs a state with the given values. */
    public CounterState(Value lastClock, Value value) {
        this.lastClock = lastClock;
        this.value = value;
    }

    /** Returns a copy of this object. */
    public Object clone() {
        // We can just use what super.clone() returns: The only instance variables are
        // Value objects, which are immutable, so we don't care that both the copy
        // and the copied refer to the same Value objects. If we had mutable instance
        // variables, then of course we would need to clone them.
        try { return super.clone(); }
        catch(CloneNotSupportedException e) { return null; }
    }
    
    /** Returns the last clock observed. */
    public Value getLastClock() {
        return lastClock;
    }
    
    /** Updates the last clock observed. */
    public void setLastClock(Value value) {
        lastClock = value;
    }
    
    /** Returns the current value emitted by the counter. */
    public Value getValue() {
        return value;
    }
    
    /** Updates the current value emitted by the counter. */
    public void setValue(Value value) {
        this.value = value;
    }
}

SimpleCounterFactory

package com.cburch.incr;

import com.cburch.logisim.comp.AbstractComponentFactory;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;

/** Manufactures simple 8-bit counters. This example illustrates how a
 * component can maintain its own internal state. All of the code relevant
 * to state, though, appears in the SimpleCounter and
 * CounterState classes. */
class SimpleCounterFactory extends AbstractComponentFactory {
    static final SimpleCounterFactory instance = new SimpleCounterFactory();
    
    private SimpleCounterFactory() { }
    
    public String getName() {
        return "Simple Counter";
    }

    public String getDisplayName() {
        return "Counter (Simple)";
    }

    public Component createComponent(Location loc, AttributeSet attrs) {
        return new SimpleCounter(loc, attrs);
    }

    public Bounds getOffsetBounds(AttributeSet arg0) {
        return Bounds.create(-30, -15, 30, 30);
    }

}

Next: Counter.