next up previous contents
Next: Implementation of the Simulation Up: Hardware Simulation Using C++ Previous: Distributed Event Queues

Signals and Signal Transmission

Every signal is composed of two elements: a signal value (for example, HIGH, LOW, X) and a time when that value was produced. They are stored in linked lists in much the same way that the Component_List class stores components.

The Signal class is declared as follows:

class Signal
{
friend ostream &operator <<(ostream &, const Signal &);
public:
        Signal(Sig_Val = X, ckt_time = INIT_TIME);
        operator        Sig_Val();
        ckt_time        get_time();
private:
        Sig_Val         value;
        ckt_time        t;
};

Note that Sig_Val is implemented as an enumerated type. The signal constructor trivially assigns a signal value and a time to the hidden members of the class.

Signal::Signal(Sig_Val sv, ckt_time ct) :
        value(sv), t(ct)
{ }

The operator Sig_Val() function is a special function called a user-defined conversion. It returns the value field whenever a signal object is used in the context where a Sig_Val is expected. This function essentially converts a signal into a signal value, and its usefulness will become apparent when the process() method for a component is described later in this chapter.

Signal::operator Sig_Val() 
{
        return value;
}

The get_time() method is simply an access method which returns the time that the signal occurred.

ckt_time
Signal::get_time() 
{
        return t;
}

The ostream &operator <<(ostream &, const Signal &) function enables the signal's value and time to be output using the << operator. This function is made a friend of the class so it has access to the hidden members of the class. Overloading this operator enables signals to be treated just like other built-in types which are output using the same technique.

The Signal_List class is declared as follows:

class Signal_List
{
public:
        Signal_List();
        void            add(Signal);
        Signal          find(ckt_time);
        void            dump();
private:
        Signal_Node     *sig_list;
};

The constructor and the add() method are identical to the corresponding methods in the Component_List class. The add() function also checks to make sure that signals enter the list in the correct time order. Should a signal be found whose time is less than or equal to the last signal on the wire, a warning message is displayed.

The find() method simply scans the nodes of the signal list searching for the signal which occurred at the specified time. If a signal could not be found, an undefined signal is returned.

Signal
Signal_List::find(ckt_time t) 
{
        // Make sure we are not looking too far in the future.
        if (sig_list->sig.get_time() < t)
                return Signal(UNDEF_SIG, UNDEF_TIME);

        for (Signal_Node *list = sig_list; list != 0; list = list->next)
        {
                if (list->sig.get_time() == t)
                        return list->sig;
                if (list->next != 0 && list->next->sig.get_time() <= t)
                        return list->next->sig;
        }

        // If signal not found, return an error signal.
        return Signal(UNDEF_SIG, UNDEF_TIME);
}

The display() method simply traverses the signal list, displaying each signal it encounters using the overloaded << operator.

void
Signal_List::dump() 
{
        for (Signal_Node *list = sig_list; list != 0; list = list->next)
        {
                cout << list->sig;
        }
}

Signal transmission occurs using the get_Signal() and send_Signal() methods which are defined as virtual methods in the Wire and Port class. These two functions were defined virtually because the method that a Port uses to get and send a signal is quite different from the method used by a Wire. A wire gets a signal by sending a find() message to its encapsulated signal list. A Port, however, gets a signal by sending a get_Signal() message to the connector which feeds it. Hence, the get_Signal() message for a wire and a port is as follows:

Signal
Wire::get_Signal(ckt_time t) 
{
        return signals.find(t);
}

Signal
Port::get_Signal(ckt_time t) 
{
        return external->get_Signal(t);
}

Note that the last call appears to be recursive with no terminating condition. However, it must be remembered that get_Signal() is a virtual function, and therefore is bound at runtime depending upon whether external is a pointer to a wire or a pointer to another port. As such, the call to get_Signal() in Port::get_Signal() will recurse only if external points to another port. Otherwise, it will call Wire::get_Signal() meaning that external points to a wire. Since every port must point directly or indirectly to a wire, it is guaranteed that this ``recursion'' will terminate.

The send_Signal() command operates in a similar fashion except that in both the wire and port class, the signal is propagated to all the components in the fan-out of the wire/port.

void
Wire::send_Signal(Signal s) 
{
        signals.add(s);
        fan_out.propagate();
}

void
Port::send_Signal(Signal s) 
{
        external->send_Signal(s);
        fan_out.propagate();
}

The details of the propagate() function are presented in the next next section along with the entire simulation algorithm.


next up previous contents
Next: Implementation of the Simulation Up: Hardware Simulation Using C++ Previous: Distributed Event Queues

Donald Craig
Sat Jul 13 16:02:11 NDT 1996