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.