/*
Copyright (C) 1998 T. Scott Dattalo
Copyright (C) 2006 Roy R Rankin
This file is part of gpsim.
gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <list>
#include <vector>
#include <sstream>
#include <math.h>
#include "../config.h"
#include "pic-processor.h"
#include "stimuli.h"
#include "stimulus_orb.h"
#include "symbol.h"
#include "interface.h"
#include "errors.h"
#include "cmd_gpsim.h"
static char num_nodes = 'a';
static char num_stimuli = 'a';
void gpsim_set_break_delta(guint64 delta, TriggerObject *f=0);
extern Processor *active_cpu;
/*
* stimulus.cc
*
* This file contains some rudimentary infrastructure to support simulating
* the environment outside of the pic. Simple net lists interconnecting pic
* I/O pins and various signal generators may be created.
*
* Details:
* There are two basic concepts behind the stimulus code: nodes and stimuli.
* The nodes are like wires and the stimuli are like sources and loads. The
* nodes define the interconnectivity between the stimuli. In most cases there
* will be only two stimuli connected by one node. For example, you may wish
* to simulate the effects of a clock input connected to porta.0 . In this case,
* the stimuli would be the external clock and the pic I/O pin.
*/
//------------------------------------------------------------------
void Stimulus_Node::new_name(const char *cPname)
{
// JRH - Perhaps this could be migrated into gpsimObject
const char *cPoldName = name().c_str();
if(name_str.empty()) {
// Assume never in symbol table.
// Every named stimulus goes into the symbol table.
gpsimObject::new_name(cPname);
symbol_table.add_stimulus_node(this);
return;
}
if(symbol_table.Exist(cPoldName)) {
// The symbol is in the symbol table. Since the
// symbol table is ordered we need to let the
// symbol table rename the object to maintain
// ordering. Yuk.
// Note that rename() will call Stimulus_Node::new_name()
// after the symbol is removed. This recursive
// call will then enter the branch that calls
// gpsimObject::new_name(). The simulus with
// its new name is added into the symbol table.
symbol_table.rename(cPoldName,cPname);
}
else {
gpsimObject::new_name(cPname);
}
}
void Stimulus_Node::new_name(string &rName)
{
new_name(rName.c_str());
}
double Stimulus_Node::get_nodeVoltage()
{
if (future_cycle) // RC calculation in progress, get current value
callback();
return(voltage);
}
void dump_node_list(void)
{
cout << "Node List\n";
Symbol_Table &ST = get_symbol_table();
Symbol_Table::node_symbol_iterator it;
Symbol_Table::node_symbol_iterator itEnd = ST.endNodeSymbol();
for(it = ST.beginNodeSymbol(); it != itEnd; it++) {
Stimulus_Node *t = (*it)->getNode();
cout << t->name() << " voltage = " << t->get_nodeVoltage() << "V\n";
if(t->stimuli)
{
stimulus *s = t->stimuli;
while(s)
{
cout << '\t' << s->name() << '\n';
s = s->next;
}
}
}
}
void dump_bus_list(void)
{
dump_node_list();
}
void add_bus(char *bus_name)
{
/*
Stimulus_Node *sn = find_node(string(node_name));
if(sn)
cout << "Warning node `" << node_name << "' is already in the node list.\n(You can't have duplicate nodes in the node list.)\n";
else
sn = new Stimulus_Node(node_name);
*/
cout << "add_bus -- not supported\n";
}
void dump_stimulus_list(void)
{
cout << "Stimulus List\n";
Symbol_Table &ST = get_symbol_table();
Symbol_Table::stimulus_symbol_iterator it;
Symbol_Table::stimulus_symbol_iterator itEnd = ST.endStimulusSymbol();
for(it = ST.beginStimulusSymbol(); it != itEnd; it++) {
stimulus *t = (*it)->getStimulus();
if(t) {
cout << t->name();
//if(t->snode)
// cout << " attached to " << t->snode->name();
t->show();
cout << '\n';
}
}
}
//========================================================================
Stimulus_Node::Stimulus_Node(const char *n)
: TriggerObject(0)
{
stimuli = 0;
nStimuli = 0;
voltage = 0;
warned = 0;
current_time_constant = 0.0;
delta_voltage = 0.0;
cap_start_cycle = 0;
future_cycle = 0;
minThreshold = 0.1; // volts
min_time_constant = 1000; // in cycles
bSettling = false;
if(n)
{
new_name(n);
}
else
{
char name_str[100];
snprintf(name_str,sizeof(name_str),"node%d",num_nodes);
num_nodes++; // %%% FIX ME %%%
new_name(name_str);
}
gi.node_configuration_changed(this);
}
Stimulus_Node::~Stimulus_Node()
{
stimulus *sptr;
sptr = stimuli;
while(sptr) {
sptr->detach(this);
sptr = sptr->next;
}
Value *vpNodeSym = symbol_table.remove(name());
if(vpNodeSym != NULL)
delete vpNodeSym;
}
Stimulus_Node * Stimulus_Node::construct(const char * psName)
{
Stimulus_Node *sn = get_symbol_table().findNode(psName);
if(sn) {
cout << "Warning node `" << psName
<< "' is already in the node list.\n"
"(You can't have duplicate nodes in the node list.)\n";
return NULL;
}
else {
sn = new Stimulus_Node(psName);
}
return sn;
}
//
// Add the stimulus 's' to the stimulus list for this node
//
void Stimulus_Node::attach_stimulus(stimulus *s)
{
stimulus *sptr;
warned = 0;
if(stimuli)
{
sptr = stimuli;
bool searching=1;
int nTotalStimuliConnected = 1;
while(searching)
{
if(s == sptr)
return; // The stimulus is already attached to this node.
nTotalStimuliConnected++;
if(sptr->next == 0)
{
sptr->next = s;
// s->next = 0; This is done below
searching=0;
}
sptr = sptr->next;
}
nStimuli = nTotalStimuliConnected;
}
else
{
stimuli = s; // This is the first stimulus attached to this node.
nStimuli = 1;
}
// If we reach this point, then it means that the stimulus that we're
// trying to attach has just been placed at the end of the the stimulus
// list for this node. So we need to 0 terminate the singly-linked list.
s->next = 0;
// Now tell the stimulus to attach itself to the node too
// (If it hasn't already.)
s->attach(this);
gi.node_configuration_changed(this);
}
//
// Search for the stimulus 's' in the stimulus list for this node.
// If it is found, then remove it from the list.
//
void Stimulus_Node::detach_stimulus(stimulus *s)
{
stimulus *sptr;
if(!s) // You can't remove a non-existant stimulus
return;
if(stimuli) {
if(s == stimuli) {
// This was the first stimulus in the list.
stimuli = s->next;
s->detach(this);
nStimuli--;
} else {
sptr = stimuli;
do {
if(s == sptr->next) {
sptr->next = s->next;
s->detach(this);
nStimuli--;
//gi.node_configuration_changed(this);
return;
}
sptr = sptr->next;
} while(sptr);
}
}
}
//------------------------------------------------------------------------
//
// Stimulus_Node::update(guint64 current_time)
//
// update() is called whenever a stimulus attached to this node changes
// states.
void Stimulus_Node::update(guint64 current_time)
{
// So far, 'update' only applies to the current time.
update();
}
//------------------------------------------------------------------------
// refresh() - compute the Thevenin voltage and Thevenin impedance
//
void Stimulus_Node::refresh()
{
if(stimuli) {
stimulus *sptr = stimuli;
initial_voltage = get_nodeVoltage();
switch (nStimuli) {
case 0:
// hmm, strange nStimuli is 0, but the stimuli pointer is non null.
break;
case 1:
// Only one stimulus is attached.
finalVoltage = sptr->get_Vth(); // RP - was just voltage
Zth = sptr->get_Zth();
break;
case 2:
// 2 stimuli are attached to the node. This is the typical case
// and we'll optimize for it.
{
stimulus *sptr2 = sptr ? sptr->next : 0;
if(!sptr2)
break; // error, nStimuli is two, but there aren't two stimuli
double V1,Z1,C1;
double V2,Z2,C2;
sptr->getThevenin(V1,Z1,C1);
sptr2->getThevenin(V2,Z2,C2);
finalVoltage = (V1*Z2 + V2*Z1) / (Z1+Z2);
Zth = Z1*Z2/(Z1+Z2);
Cth = C1+C2;
}
break;
default:
{
/*
There are 3 or more stimuli connected to this node. Recall
that these are all in parallel. The Thevenin voltage and
impedance for this is:
Thevenin impedance:
Zt = 1 / sum(1/Zi)
Thevenin voltage:
Vt = sum( Vi / ( ((Zi - Zt)/Zt) + 1) )
= sum( Vi * Zt /Zi)
= Zt * sum(Vi/Zi)
*/
double conductance=0.0; // Thevenin conductance.
Cth=0;
finalVoltage=0.0;
//cout << "multi-node summing:\n";
while(sptr) {
double V1,Z1,C1;
sptr->getThevenin(V1,Z1,C1);
/*
cout << " N: " <<sptr->name()
<< " V=" << V1
<< " Z=" << Z1
<< " C=" << C1 << endl;
*/
double Cs = 1 / Z1;
finalVoltage += V1 * Cs;
conductance += Cs;
Cth += C1;
sptr = sptr->next;
}
Zth = 1.0/conductance;
finalVoltage *= Zth;
}
}
current_time_constant = Cth * Zth;
if(((guint64)(current_time_constant*get_cycles().instruction_cps())
< min_time_constant)
||
(fabs(finalVoltage - voltage) < minThreshold))
{
if (verbose)
cout << "Stimulus_Node::refresh " << name() << " use DC " <<
finalVoltage << " as current_time_constant=" <<
current_time_constant << endl;
if (future_cycle) // callback is active
{
get_cycles().clear_break(this);
}
voltage = finalVoltage;
future_cycle = 0;
} else {
settlingTimeStep = (guint64) (0.11 *
get_cycles().instruction_cps() * current_time_constant);
voltage = initial_voltage;
if (verbose)
cout << "Stimulus_Node::refresh " << name() << " settlingTimeStep="
<< settlingTimeStep << " voltage=" << voltage << " Finalvoltage="
<< finalVoltage << endl;
/*
If future_cycle is not 0 we are in the middle of an RC
calculation, but an input condition has changed.
*/
if (future_cycle)
{
callback();
}
else
{
cap_start_cycle = get_cycles().value;
future_cycle = cap_start_cycle + settlingTimeStep;
get_cycles().set_break(future_cycle,this);
}
}
}
}
//------------------------------------------------------------------------
// updateStimuli
//
// drive all the stimuli connected to this node.
void Stimulus_Node::updateStimuli()
{
stimulus *sptr = stimuli;
while(sptr) {
sptr->set_nodeVoltage(voltage);
sptr = sptr->next;
}
}
void Stimulus_Node::update()
{
if(stimuli) {
refresh();
updateStimuli();
}
}
void Stimulus_Node::set_nodeVoltage(double v)
{
voltage = v;
updateStimuli();
}
//------------------------------------------------------------------------
void Stimulus_Node::callback()
{
if (verbose)
callback_print();
initial_voltage = voltage;
double Time_Step;
double expz;
//
// increase time step as capacitor charges more slowly as final
// voltage is approached.
//
//
// The following is an exact calculation, assuming no circuit
// changes, regardless of time step.
//
Time_Step = (get_cycles().value - cap_start_cycle)/
(get_cycles().instruction_cps()*current_time_constant);
expz = exp(-Time_Step);
voltage = finalVoltage* (1.-expz) + voltage * expz;
if (verbose)
cout << "\tVoltage was " << initial_voltage << "V now "
<< voltage << "V\n";
if (fabs(finalVoltage - voltage) < minThreshold) {
voltage = finalVoltage;
future_cycle = 0;
if (verbose)
cout << "\t" << name() <<
" Final voltage " << finalVoltage << " reached at "
<< get_cycles().value << " cycles\n";
}
else if(get_cycles().value >= future_cycle) // got here via break
{
settlingTimeStep = (guint64) (1.5 * settlingTimeStep);
cap_start_cycle = get_cycles().value;
future_cycle = cap_start_cycle + settlingTimeStep;
get_cycles().set_break(future_cycle, this);
if (verbose)
cout << "\tBreak reached at " << cap_start_cycle <<
" cycles, next break set for "
<< future_cycle << " delta=" << settlingTimeStep << endl;
}
else // updating value before break don't increase step size
{
cap_start_cycle = get_cycles().value;
get_cycles().reassign_break(future_cycle,
cap_start_cycle + settlingTimeStep, this);
future_cycle = get_cycles().value + settlingTimeStep;
if (verbose)
cout << "\tcallback called at " << cap_start_cycle <<
" cycles, next break set for " << future_cycle << " delta="
<< settlingTimeStep << endl;
}
updateStimuli();
}
//------------------------------------------------------------------------
void Stimulus_Node::callback_print()
{
cout << "Node: " << name() ;
TriggerObject::callback_print();
}
//------------------------------------------------------------------------
void Stimulus_Node::time_constant(double new_tc)
{
min_time_constant = (unsigned int)(new_tc*get_cycles().instruction_cps());
}
//------------------------------------------------------------------------
stimulus::stimulus(const char *cPname,double _Vth, double _Zth)
: snode(NULL), next(NULL), Vth(_Vth), Zth(_Zth)
{
if(cPname && *cPname)
new_name(cPname);
snode = 0;
bDrivingState = false;
bDriving = false;
next = 0;
Cth = 0; // Farads
nodeVoltage = 0.0; // Volts
}
void stimulus::new_name(const char *cPname)
{
const char *cPoldName = name().c_str();
if(name_str.empty() && cPname != NULL && *cPname != 0) {
// Assume never in symbol table.
// Every named stimulus goes into the symbol table.
gpsimObject::new_name(cPname);
symbol_table.add_stimulus(this);
return;
}
if(symbol_table.Exist(cPoldName)) {
// The symbol is in the symbol table. Since the
// symbol table is ordered we need to let the
// symbol table rename the object to maintain
// ordering. Yuk.
// Note that rename() will call stimulus::new_name()
// after the symbol is removed. This recursive
// call will then enter the branch that calls
// gpsimObject::new_name(). The simulus with
// its new name is added into the symbol table.
symbol_table.rename(cPoldName,cPname);
}
else {
gpsimObject::new_name(cPname);
}
}
void stimulus::new_name(string &rName)
{
new_name(rName.c_str());
}
stimulus::~stimulus(void)
{
if(snode)
snode->detach_stimulus(this);
Value *vpNodeSym = symbol_table.remove(name());
if(vpNodeSym != NULL)
delete vpNodeSym;
}
void stimulus::show()
{
GetUserInterface().DisplayMessage(toString().c_str());
}
string stimulus::toString()
{
ostringstream s;
s << " stimulus ";
if(snode)
s << " attached to " << snode->name();
s << endl
<< " Vth=" << get_Vth() << "V"
<< " Zth=" << get_Zth() << " ohms"
<< " Cth=" << get_Cth() << "F"
<< " nodeVoltage= " << get_nodeVoltage() << "V"
<< endl
<< " Driving=" << getDriving()
<< " drivingState=" << getDrivingState()
<< " drivenState=" << getDrivenState()
<< " bitState=" << getBitChar();
return s.str();
}
void stimulus::attach(Stimulus_Node *s)
{
detach(snode);
snode = s;
}
void stimulus::detach(Stimulus_Node *s)
{
if(snode == s)
snode = 0;
}
void stimulus::getThevenin(double &v, double &z, double &c)
{
v = get_Vth();
z = get_Zth();
c = get_Cth();
}
//========================================================================
//
PinMonitor::PinMonitor()
{
}
PinMonitor::~PinMonitor()
{
}
void PinMonitor::addSink(SignalSink *new_sink)
{
if(new_sink)
sinks.push_back(new_sink);
}
void PinMonitor::removeSink(SignalSink *pSink)
{
if(pSink)
sinks.remove(pSink);
}
//========================================================================
square_wave::square_wave(unsigned int p, unsigned int dc, unsigned int ph, const char *n)
{
//cout << "creating sqw stimulus\n";
if(n)
new_name(n);
else
{
char name_str[100];
snprintf(name_str,sizeof(name_str),"s%d_square_wave",num_stimuli);
num_stimuli++;
new_name(name_str);
}
period = p; // cycles
duty = dc; // # of cycles over the period for which the sq wave is high
phase = ph; // phase of the sq wave wrt the cycle counter
time = 0; // simulation time
snode = 0;
next = 0;
}
double square_wave::get_Vth()
{
guint64 current_time = get_cycles().value;
if(verbose & 1)
cout << "Getting new state of the square wave.\n";
if( ((current_time+phase) % period) <= duty)
return Vth;
else
return 0.0;
}
//========================================================================
//
// triangle_wave
triangle_wave::triangle_wave(unsigned int p, unsigned int dc, unsigned int ph, const char *n)
{
//cout << "creating sqw stimulus\n";
if(n)
new_name(n);
else
{
char name_str[100];
snprintf(name_str,sizeof(name_str),"s%d_triangle_wave",num_stimuli);
num_stimuli++;
new_name(name_str);
}
if(p==0) //error
p = 1;
// copy the square wave stuff
period = p; // cycles
duty = dc; // # of cycles over the period for which the sq wave is high
phase = ph; // phase of the sq wave wrt the cycle counter
time = 0; // simulation time
snode = 0;
next = 0;
//cout << "duty cycle " << dc << " period " << p << " drive " << drive << '\n';
// calculate the slope and the intercept for the two lines comprising
// the triangle wave:
if(duty)
m1 = Vth/duty;
else
m1 = Vth/period; // m1 will not be used if the duty cycle is zero
b1 = 0;
if(period != duty)
m2 = Vth/(duty - period);
else
m2 = Vth;
b2 = -m2 * period;
//cout << "m1 = " << m1 << " b1 = " << b1 << '\n';
//cout << "m2 = " << m2 << " b2 = " << b2 << '\n';
}
double triangle_wave::get_Vth()
{
guint64 current_time = get_cycles().value;
//cout << "Getting new state of the triangle wave.\n";
guint64 t = (current_time+phase) % period;
double ret_val;
if( t <= duty)
ret_val = b1 + m1 * t;
else
ret_val = b2 + m2 * t;
// cout << "Triangle wave: t = " << t << " value = " << ret_val << '\n';
return ret_val;
}
//========================================================================
//
// Event
Event::Event(void)
{
current_state = 0;
}
//========================================================================
//
void Event::callback(void)
{
// If there's a node attached to this stimulus, then update it.
if(snode)
snode->update();
// If the event is inactive.
if(current_state == 0) {
get_cycles().set_break_delta(1,this);
current_state = 1;
} else {
current_state = 0;
}
}
void source_stimulus::callback_print(void)
{
cout << "stimulus " << name() << " CallBack ID " << CallBackID << '\n';
}
void source_stimulus::callback(void)
{
cout << "shouldn't be called\n";
}
void source_stimulus::show()
{
stimulus::show();
}
void source_stimulus::put_period(Value *pValue)
{
if (pValue)
pValue->get(period);
}
void source_stimulus::put_duty(Value *pValue)
{
if (pValue)
pValue->get(duty);
}
void source_stimulus::put_phase(Value *pValue)
{
if (pValue)
pValue->get(phase);
}
void source_stimulus::put_initial_state(Value *pValue)
{
if (pValue)
pValue->get(initial_state);
}
void source_stimulus::put_start_cycle(Value *pValue)
{
if (pValue)
pValue->get(start_cycle);
}
//========================================================================
//
IOPIN::IOPIN(const char *_name,
double _Vth,
double _Zth,
double _ZthWeak,
double _ZthFloating
)
: stimulus(_name,_Vth, _Zth),
ZthWeak(_ZthWeak), ZthFloating(_ZthFloating)
{
if(verbose)
cout << "IOPIN default constructor\n";
l2h_threshold = 2.0; // PICs are CMOS and use CMOS-like thresholds
h2l_threshold = 1.0;
bDrivenState = false;
cForcedDrivenState = 'Z';
snode = 0;
m_monitor=0;
}
void IOPIN::setMonitor(PinMonitor *new_pinMonitor)
{
if (!m_monitor && new_pinMonitor)
m_monitor = new_pinMonitor;
}
IOPIN::~IOPIN()
{
}
void IOPIN::attach(Stimulus_Node *s)
{
snode = s;
}
void IOPIN::show()
{
stimulus::show();
}
//--------------------
// set_nodeVoltage()
//
//
void IOPIN::set_nodeVoltage(double new_nodeVoltage)
{
if(verbose & 1)
cout << name()<< " set_nodeVoltage old="<<nodeVoltage <<" new="<<new_nodeVoltage<<endl;
nodeVoltage = new_nodeVoltage;
if( nodeVoltage < h2l_threshold) {
// The voltage is below the low threshold
setDrivenState(false);
}
else if(nodeVoltage > l2h_threshold) {
// The voltage is above the high threshold
setDrivenState(true);
} else {
// The voltage is between the low and high thresholds,
// so do nothing
}
//setDrivenState(getBitChar());
if (m_monitor)
m_monitor->set_nodeVoltage(nodeVoltage);
}
//------------------------------------------------------------
// putState - called by peripherals when they wish to
// drive an I/O pin to a new state.
void IOPIN::putState(bool new_state)
{
if(new_state != bDrivingState) {
bDrivingState = new_state;
Vth = bDrivingState ? 5.0 : 0.3;
if(verbose & 1)
cout << name()<< " putState= "
<< (new_state ? "high" : "low") << endl;
// If this pin is tied to a node, then update the node.
// Note that when the node is updated, then the I/O port
// (if there is one) holding this I/O pin will get updated.
// If this pin is not tied to a node, then try to update
// the I/O port directly.
if(snode)
snode->update();
}
if(m_monitor)
m_monitor->putState(new_state?'1':'0');
}
//------------------------------------------------------------
bool IOPIN::getState()
{
return getDriving() ? getDrivingState() : getDrivenState();
}
void IOPIN::setDrivingState(bool new_state)
{
bDrivingState = new_state;
if(m_monitor)
m_monitor->setDrivingState(bDrivingState?'1':'0');
if(verbose & 1)
cout << name()<< " setDrivingState= "
<< (new_state ? "high" : "low") << endl;
}
void IOPIN::setDrivingState(char new3State)
{
bDrivingState = new3State=='1';
if(m_monitor)
m_monitor->setDrivingState(new3State);
}
bool IOPIN::getDrivingState(void)
{
return bDrivingState;
}
bool IOPIN::getDrivenState()
{
return bDrivenState;
}
//------------------------------------------------------------------------
// setDrivenState
//
// An stimulus attached to this pin is driving us to a new state.
// This state will be recorded and propagate up to anything
// monitoring this pin.
void IOPIN::setDrivenState(bool new_state)
{
bDrivenState = new_state;
if(verbose & 1)
cout << name()<< " setDrivenState= "
<< (new_state ? "high" : "low") << endl;
// Propagate the new state to those things monitoring this pin.
// (note that the 3-state value is what's propagated).
if(m_monitor)
{
m_monitor->setDrivenState(getBitChar());
if(verbose & 16)
cout << name() << " setting state of monitor to " << getBitChar() << endl;
}
}
//------------------------------------------------------------------------
// forceDrivenState() - allows the 'driven state' to be manipulated whenever
// there is no snode attached. The primary purpose of this is to allow the
// UI to toggle I/O pin states.
//
void IOPIN::forceDrivenState(char newForcedState)
{
if (cForcedDrivenState != newForcedState) {
cForcedDrivenState = newForcedState;
bDrivenState = cForcedDrivenState=='1' || cForcedDrivenState=='W';
if(m_monitor) {
m_monitor->setDrivenState(getBitChar());
m_monitor->updateUI();
}
}
}
char IOPIN::getForcedDrivenState()
{
return cForcedDrivenState;
}
void IOPIN::toggle()
{
putState(getState() ^ true);
}
/*************************************
* int IOPIN::get_Vth()
*
* If this iopin has a stimulus attached to it then
* the voltage will be dictated by the stimulus. Otherwise,
* the voltage is determined by the state of the ioport register
* that is inside the pic. For an input (like this), the pic code
* that is being simulated can not change the state of the I/O pin.
* However, the user has the ability to modify the state of
* this register either by writing directly to it in the cli,
* or by clicking in one of many places in the gui.
*/
double IOPIN::get_Vth()
{
return Vth;
}
char IOPIN::getBitChar()
{
if(!snode)
return getForcedDrivenState(); // RCP - Changed to match IO_bi_directional
// was return 'Z'; // High impedance - unknown state.
if(snode->get_nodeZth() > ZthFloating)
return 'Z';
if(snode->get_nodeZth() > ZthWeak)
return getDrivenState() ? 'W' : 'w';
return getDrivenState() ? '1' : '0';
}
void IOPIN::newGUIname(const char *s)
{
if(s)
{
gui_name_updated = true;
gui_name = string(s);
}
}
string &IOPIN::GUIname(void) const
{
return (string &)gui_name;
}
//========================================================================
//
IO_bi_directional::IO_bi_directional(const char *_name,
double _Vth,
double _Zth,
double _ZthWeak,
double _ZthFloating,
double _VthIn,
double _ZthIn)
: IOPIN(_name, _Vth, _Zth, _ZthWeak, _ZthFloating),
ZthIn(_ZthIn), VthIn(_VthIn)
{
}
void IO_bi_directional::set_nodeVoltage( double new_nodeVoltage)
{
IOPIN::set_nodeVoltage(new_nodeVoltage);
}
double IO_bi_directional::get_Vth()
{
if(getDriving())
return getDrivingState() ? Vth : 0;
//return getDrivingState() ? VthIn : 0;
return VthIn;
}
double IO_bi_directional::get_Zth()
{
return getDriving() ? Zth : ZthIn;
}
/*
getBitChar() returns bit status as follows
Input pin
1> Pin considered floating,
return 'Z'
2> Weak Impedance on pin,
return 'W" if high or 'w' if low
3> Pin being driven externally
return '1' node voltage high '0' if low
Output pin
1> Node voltage opposite driven value
return 'X' if node voltage high or 'x' if inode voltage low
2> Node voltage same as driven value
return '1' node voltage high '0' if low
*/
char IO_bi_directional::getBitChar()
{
if(!snode && !getDriving() )
return getForcedDrivenState();
if(snode) {
if (!getDriving()) // input pin
{
if(snode->get_nodeZth() > ZthFloating)
return 'Z';
if(snode->get_nodeZth() > ZthWeak)
return getDrivenState() ? 'W' : 'w';
}
else if(getDrivenState() != getDrivingState())
return getDrivenState() ? 'X' : 'x';
}
return getDrivenState() ? '1' : '0';
}
//---------------
//::update_direction(unsigned int new_direction)
//
// This is called when a new value is written to the tris register
// with which this bi-direction pin is associated.
void IO_bi_directional::update_direction(unsigned int new_direction, bool refresh)
{
setDriving(new_direction ? true : false);
// If this pin is not associated with an IO Port, but it's tied
// to a stimulus, then we need to update the stimulus.
if(refresh && snode)
snode->update();
}
IO_bi_directional_pu::IO_bi_directional_pu(const char *_name,
double _Vth,
double _Zth,
double _ZthWeak,
double _ZthFloating,
double _VthIn,
double _ZthIn,
double _Zpullup)
: IO_bi_directional(_name, _Vth, _Zth, _ZthWeak,
_ZthFloating, _VthIn, _ZthIn),
Zpullup(_Zpullup)
{
Vpullup = Vth;
bPullUp = false;
}
IO_bi_directional_pu::~IO_bi_directional_pu(void)
{
}
void IO_bi_directional_pu::update_pullup(char new_state, bool refresh)
{
bool bNewPullupState = new_state == '1' || new_state == 'W';
if (bPullUp != bNewPullupState) {
bPullUp = bNewPullupState;
if (refresh) {
if (snode)
snode->update();
else
setDrivenState(bPullUp);
}
}
}
double IO_bi_directional_pu::get_Zth()
{
return getDriving() ? Zth : (bPullUp ? Zpullup : ZthIn);
}
double IO_bi_directional_pu::get_Vth()
{
/**/
if(verbose & 1)
cout << name() << " get_Vth PU "
<< " driving=" << getDriving()
<< " DrivingState=" << getDrivingState()
<< " bDrivenState=" << bDrivenState
<< " Vth=" << Vth
<< " VthIn=" << VthIn
<< " bPullUp=" << bPullUp << endl;
/**/
// If the pin is configured as an output, then the driving voltage
// depends on the pin state. If the pin is an input, and the pullup resistor
// is enabled, then the pull-up resistor will 'drive' the output. The
// open circuit voltage in this case will be Vth (the thevenin voltage,
// which is assigned to be same as the processor's supply voltage).
if(getDriving())
return getDrivingState() ? Vth : 0;
else
return bPullUp ? Vpullup : VthIn;
}
/*
getBitChar() returns bit status as follows
Input pin
1> Pin considered floating,
return 'Z'
2> Weak Impedance on pin,
return 'W" if high or 'w' if low
3> Pin being driven externally
return '1' node voltage high '0' if low
Output pin
1> Node voltage opposite driven value
return 'X' if node voltage high or 'x' if inode voltage low
2> Node voltage same as driven value
return '1' node voltage high '0' if low
*/
char IO_bi_directional_pu::getBitChar()
{
if(!snode && !getDriving() ) {
char cForced=getForcedDrivenState();
return (cForced=='Z' && bPullUp) ? 'W' : cForced;
}
if(snode) {
if (!getDriving()) // input pin
{
if(snode->get_nodeZth() > ZthFloating)
return 'Z';
if(snode->get_nodeZth() > ZthWeak)
return getDrivenState() ? 'W' : 'w';
}
else if(getDrivenState() != getDrivingState())
return getDrivenState() ? 'X' : 'x';
}
return getDrivenState() ? '1' : '0';
}
IO_open_collector::IO_open_collector(const char *_name)
: IO_bi_directional_pu(_name)
{
}
double IO_open_collector::get_Vth()
{
/**/
if(verbose & 1)
cout << name() << " get_Vth OC"
<< " driving=" << getDriving()
<< " DrivingState=" << getDrivingState()
<< " bDrivenState=" << bDrivenState
<< " Vth=" << Vth
<< " VthIn=" << VthIn
<< " bPullUp=" << bPullUp << endl;
/**/
if(getDriving() && !getDrivingState())
return 0.0;
return bPullUp ? Vpullup : VthIn;
}
double IO_open_collector::get_Zth()
{
if(getDriving() && !getDrivingState())
return Zth;
return bPullUp ? Zpullup : ZthIn;
}
char IO_open_collector::getBitChar()
{
if(!snode && !getDriving() ){
char cForced=getForcedDrivenState();
return (cForced=='Z' && bPullUp) ? 'W' : cForced;
}
if(snode) {
if(snode->get_nodeZth() > ZthFloating)
return bPullUp ? 'W' : 'Z';
if(getDriving() && getDrivenState() && !getDrivingState())
return 'X';
if(snode->get_nodeZth() > ZthWeak)
return getDrivenState() ? 'W' : 'w';
else
return getDrivenState() ? '1' : '0';
}
return getDrivingState() ? 'W' : '0';
}
//========================================================================
//========================================================================
//
// ValueStimulus
//
// A Value stimulus is a stream of data that can change values at
// arbitrary times. An array called 'transition_cycles' stores the times
// and an array 'values' stores the values.
// When first initialized, the stimulus is driven to its initial state.
// A break point is set on the cycle counter for the next cpu cycle that
// the stimulus is expected to change values. When the break occurs,
// the current state is updated to the next value and then a break is set
// for the next expect change. This cycle occurs until all of the values
// have been generated. When the end is reached, the asynchronous stimulus
// will restart from the beginning. The member variable 'period' describes
// the magnitude of the rollover (if it's zero then there is no rollover).
//
ValueStimulus::ValueStimulus(const char *n)
: source_stimulus()
{
initial.time = 0;
initial.v = 0;
current = 0;
if(n)
new_name(n);
else
{
char name_str[100];
snprintf(name_str,sizeof(name_str),"s%d_asynchronous_stimulus",num_stimuli);
num_stimuli++;
new_name(name_str);
}
}
ValueStimulus::~ValueStimulus()
{
delete initial.v;
delete current;
for(sample_iterator = samples.begin();
sample_iterator != samples.end();
++sample_iterator) {
delete (*sample_iterator).v;
}
}
void ValueStimulus::show()
{
// print the electrical stuff
stimulus::show();
cout << "\n states = " << samples.size() << '\n';
list<ValueStimulusData>::iterator si;
for(si = samples.begin();
si != samples.end();
++si) {
//double d;
//(*si).v->get(d);
cout << " t=" << dec << (*si).time
<< ",v=" << (*si).v->toString()
<< '\n';
}
if (initial.v)
cout << " initial=" << initial.v->toString() << '\n';
cout
<< " period=" << period << '\n'
<< " start_cycle=" << start_cycle << '\n'
<< " Next break cycle=" << future_cycle << '\n';
}
void ValueStimulus::callback()
{
guint64 current_cycle = future_cycle;
current = next_sample.v;
if(verbose & 1)
cout << "asynchro cycle " << current_cycle << " state " << current->toString() << '\n';
// If there's a node attached to this stimulus, then update it.
if(snode)
snode->update();
ValueStimulusData *n = getNextSample();
if(n) {
next_sample = *n;
if(verbose & 1) {
cout << " current_sample (" << next_sample.time << ","
<< next_sample.v->toString() << ")\n";
cout << " start cycle " << start_cycle << endl;
}
// get the cycle when the data will change next
future_cycle = next_sample.time + start_cycle;
if(future_cycle <= current_cycle) {
// There's an error in the data. Set a break on the next simulation cycle
// and see if it can be resolved.
future_cycle = current_cycle+1;
}
get_cycles().set_break(future_cycle, this);
} else
future_cycle = 0;
if(verbose & 1)
cout <<" next transition = " << future_cycle << '\n';
}
void ValueStimulus::put_initial_state(Value *pValue)
{
if (pValue && !initial.v) {
initial.time = 0;
initial.v = pValue->copy();
}
}
void ValueStimulus::put_data(ValueStimulusData &data_point)
{
ValueStimulusData *sample = new ValueStimulusData;
sample->time = data_point.time;
sample->v = data_point.v;
samples.push_back(*sample);
}
double ValueStimulus::get_Vth()
{
double v=initial_state;
if(current) {
try {
current->get(v);
if(digital && v >0.0)
v = 5.0;
}
catch (Error *err) {
if(err) {
cout << "Warning stimulus: " << name() << " failed on: "<< err->toString() << endl;
delete err;
}
}
}
return v;
}
void ValueStimulus::start()
{
if(verbose & 1)
cout << "Starting asynchronous stimulus\n";
if(period) {
// Create a data point for the rollover condition.
// If an initial value was supplied when the stimulus was created, then
// that's what we'll use for the rollover. Otherwise, we'll create
// a rollover based on 'initial_state' (which should be a default value).
ValueStimulusData vSample;
vSample.time = period;
vSample.v = initial.v ? initial.v : new Float(initial_state);
put_data(vSample);
}
sample_iterator = samples.begin();
if(sample_iterator != samples.end()) {
if(digital)
initial_state = (initial_state > 0.0) ? Vth : 0.0;
current = initial.v;
next_sample = *sample_iterator;
future_cycle = next_sample.time;// + start_cycle;
get_cycles().set_break(future_cycle, this);
}
if(verbose & 1)
cout << "asy should've been started\n";
}
ValueStimulusData *ValueStimulus::getNextSample()
{
++sample_iterator;
if(sample_iterator == samples.end()) {
// We've gone through all of the data. Now let's try to start over
sample_iterator = samples.begin();
// If the period is zero then we don't want to
// regenerate the data stream.
if(period == 0)
return 0;
start_cycle += period;
if(verbose & 1) {
cout << " asynchronous stimulus rolled over\n"
<< " next start_cycle " << start_cycle << " period " << period << '\n';
}
}
return &(*sample_iterator);
}
//------------------------------------------------------------------------
AttributeStimulus::AttributeStimulus(const char *n)
: ValueStimulus(n), attr(0)
{
}
/*
AttributeStimulus::~AttributeStimulus()
{
ValueStimulus::~ValueStimulus();
}
*/
void AttributeStimulus::show()
{
if (attr)
cout << "\nDriving Attribute:" << attr->name() << endl;
ValueStimulus::show();
}
void AttributeStimulus::callback()
{
guint64 current_cycle = future_cycle;
current = next_sample.v;
if(verbose & 1)
cout << "asynchro cycle " << current_cycle << " state " << current->toString() << '\n';
// If there's a node attached to this stimulus, then update it.
if(attr)
attr->set(current);
ValueStimulusData *n = getNextSample();
if(n) {
next_sample = *n;
if(verbose & 1) {
cout << " current_sample (" << next_sample.time << ","
<< next_sample.v->toString() << ")\n";
cout << " start cycle " << start_cycle << endl;
}
// get the cycle when the data will change next
future_cycle = next_sample.time + start_cycle;
if(future_cycle <= current_cycle) {
// There's an error in the data. Set a break on the next simulation cycle
// and see if it can be resolved.
future_cycle = current_cycle+1;
}
get_cycles().set_break(future_cycle, this);
} else
future_cycle = 0;
if(verbose & 1)
cout <<" next transition = " << future_cycle << '\n';
}
void AttributeStimulus::setClientAttribute(Value *v)
{
if(attr)
cout << "overwriting target attribute in AttributeStimulus\n";
attr = v;
if((bool)verbose && v)
cout << " attached " << name() << " to attribute: " << v->name() << endl;
}
//========================================================================
//
// helper functions follow here
//--------------------------------------------------------
// Char list.
// Here's a singly linked-list of char *'s.
struct char_list {
char *name;
char_list *next;
};
void stimorb_attach(char *node, char_list *stimuli)
{
if(verbose&2)
cout << " doing an attach (stimuli.cc) node: " << node << '\n';
if(!node)
return;
string s(node);
Symbol_Table &ST = get_symbol_table();
Stimulus_Node *sn = ST.findNode(s);
if(sn) {
while(stimuli) {
s = string(stimuli->name);
stimulus *st = ST.findStimulus(s);
if(st) {
sn->attach_stimulus(st);
if(verbose&2)
cout << " attaching stimulus: " << s << '\n';
}
else
cout << "Warning, stimulus: " << s << " not attached\n";
stimuli = stimuli->next;
}
sn->update();
}
else {
cout << "Warning: Node \"" << node << "\" was not found in the node list\n";
}
}
void AttachStimulusToNode(Stimulus_Node *sn, string &sStimulusName);
void AttachStimulusToNode(Stimulus_Node *sn, string &sStimulusName,
stimulus *st);
//========================================================================
// stimuli_attach(list <string> * sl)
//
// Attach stimuli to a node
//
// The first item in the input list is the name of the node.
// The remaining items are the names of the stimuli.
void stimuli_attach(StringList_t *sl)
{
if (!sl)
return;
list <string> :: iterator si;
si = sl->begin();
Symbol_Table &ST = get_symbol_table();
Stimulus_Node *sn = ST.findNode((*si));
if(sn) {
for(++si; si != sl->end(); ++si)
{
AttachStimulusToNode(sn, *si);
}
sn->update();
}
else {
cout << "Warning: Node \"" << (*si) << "\" was not found in the node list\n";
}
}
void stimuli_attach(SymbolList_t *sl)
{
if (!sl)
return;
SymbolList_t :: iterator si;
// The first symbol is always the node name
si = sl->begin();
Symbol_Table &ST = get_symbol_table();
Stimulus_Node *sn = ST.findNode((*si)->name());
if(sn) {
// All symbols thereafter are stimulus objects
for(++si; si != sl->end(); ++si)
{
AttachStimulusToNode(sn, (*si)->name());
}
sn->update();
}
else {
// The first symbol is not a node - so let's assume that
// we're performing a register stimulus.
//cout << "Warning: Node \"" << (*si) << "\" was not found in the node list\n";
stimulus *st;
Value *v;
if(sl->size() == 2) {
st = ST.findStimulus((*si)->name());
if(st) {
++si;
v = *si;
} else {
v = *si;
++si;
st = ST.findStimulus((*si)->name());
}
if(st) {
AttributeStimulus *ast = dynamic_cast<AttributeStimulus *>(st);
if(ast)
ast->setClientAttribute(v);
}
}
}
}
void stimuli_attach(Value *pNode, PinList_t *pPinList)
{
bool bSuccess = true;
Symbol_Table &ST = get_symbol_table();
node_symbol *pNS = dynamic_cast<node_symbol*>(pNode); // ST.findNode(pNode->name());
PinList_t::iterator si;
if(pNS) {
Stimulus_Node *sn = pNS->getNode();
// All symbols thereafter are stimulus objects
for(si = pPinList->begin();
si != pPinList->end() && bSuccess; ++si)
{
Pin_t * pPinArgument = *si;
#if 0
// don't have time to test out this new structure.
stimulus * pStim = pPinArgument->GetStimulus();
if(pStim != NULL) {
// PinName symbol name only
AttachStimulusToNode(sn, pStim->name(), pStim);
}
else {
IOPIN * pPin = pPinArgument->GetIOPin();
AttachStimulusToNode(sn, pPinArgument->m_sPin->name(), pPin);
}
#else
stimulus_symbol * pPinSymbol = dynamic_cast<stimulus_symbol*>(pPinArgument->m_sPin);
stimulus * pPin = pPinSymbol == NULL ? NULL : pPinSymbol->getStimulus();
if(pPin != NULL) {
// PinName symbol name only
AttachStimulusToNode(sn, pPin->name(), pPin);
}
else {
Module *pMod = NULL;
if(pPinArgument->m_iFlags & Pin_t::eActiveProc) {
pMod = get_active_cpu();
}
else if ( pPinArgument->m_sModuleName ) {
// this dynamic_cast always fails here
pMod = dynamic_cast<Module*>(pPinArgument->m_sModuleName);
if(pMod == NULL) {
// but the dynamic_cast in findModule() succeeds
pMod = ST.findModule(pPinArgument->m_sModuleName->name().c_str());
if(pMod == NULL) {
String *pModName = dynamic_cast<String*>(pPinArgument->m_sModuleName);
// but the dynamic_cast in findModule() succeeds
if(pModName != NULL) {
pMod = ST.findModule(*pModName);
}
}
}
}
if(pMod == NULL) {
if (NULL == pPinArgument->m_sModuleName) {
GetUserInterface().DisplayMessage(
"attach error: did not find pin '%s'\n",
pPinArgument->m_sPin->name().c_str());
}
else {
GetUserInterface().DisplayMessage(
"attach error: did not find module '%s'\n",
pPinArgument->m_sModuleName->name().c_str());
}
bSuccess = false;
}
else {
Integer *pPinInt = dynamic_cast<Integer*>(pPinArgument->m_sPin);
if(pPinInt != NULL) {
IOPIN *pPinObj = NULL;
if(pPinArgument->m_iFlags & Pin_t::ePackageBased) {
// ModName && Integer
// Could be a literal int or a symbol
pPinObj = pMod->get_pin(*pPinInt);
}
else /* if(pPinArgument->m_iFlags & Pin_t::ePortBased) */ {
ioport_symbol *pIOPSym = dynamic_cast<ioport_symbol*>(pPinArgument->m_sPort);
if(pIOPSym != NULL) {
PortRegister * pPort = pIOPSym->getIOPort();
pPinObj = pPort->getPin(*pPinInt);
}
else {
bSuccess = false;
GetUserInterface().DisplayMessage(
"attach error: did not find port '%s' in module '%s'\n",
pPinArgument->m_sPort->name().c_str());
}
}
if(pPinObj != NULL) {
AttachStimulusToNode(sn, pPinInt->name(), pPinObj);
}
else {
bSuccess = false;
GetUserInterface().DisplayMessage(
"attach error: did not find pin '%d' in module '%s'\n",
(int)*pPinInt, pMod->name().c_str());
}
}
else {
bSuccess = false;
if(pPin == NULL &&
strcmp(pPinArgument->m_sPin->showType().c_str(), "module_symbol")) {
int iValue = -1;
if(pPinArgument->m_sPin) {
pPinArgument->m_sPin->get(iValue);
}
if(pPinArgument->m_sPort) {
pPinArgument->m_sPort->get(iValue);
}
GetUserInterface().DisplayMessage(
"attach error: pin argument '%s'(%d) type(%s) is not of type Integer or stimulus\n",
pPinArgument->m_sPin->name().c_str(), iValue,
pPinArgument->m_sPin->showType().c_str());
}
else {
GetUserInterface().DisplayMessage(
"attach error: pin argument '%s' type(%s) is not of type Integer or stimulus\n",
pPinArgument->m_sPin->name().c_str(),
pPinArgument->m_sPin->showType().c_str());
}
}
}
}
#endif
}
sn->update();
}
else {
// The first symbol is not a node - so let's assume that
// we're performing a register stimulus.
//cout << "Warning: Node \"" << (*si) << "\" was not found in the node list\n";
si = pPinList->begin();
stimulus *st;
Value *v;
if(pPinList->size() == 1) {
// Might be a stimulus but probably not
st = dynamic_cast<stimulus*>(pNode);
if(st == NULL) {
// Might be an attribute holding a stimulus
st = ST.findStimulus(pNode->name());
}
Pin_t *pPin = dynamic_cast<Pin_t*>(*si);
if(st) {
// if pNode is a stimulus
// then get whatever the the first Pin_t is
v = pPin->GetValue();
} else {
v = pNode;
st = ST.findStimulus(pPin->GetValue()->name());
}
if(st) {
AttributeStimulus *ast = dynamic_cast<AttributeStimulus *>(st);
if(ast) {
ast->setClientAttribute(v);
}
}
}
}
}
void AttachStimulusToNode(Stimulus_Node *sn, string &sStimulusName) {
stimulus *st = get_symbol_table().findStimulus(sStimulusName);
AttachStimulusToNode(sn, sStimulusName, st);
}
void AttachStimulusToNode(Stimulus_Node *sn, string &sStimulusName, stimulus *st) {
if(st) {
// attach each found stimulus to the node
sn->attach_stimulus(st);
if(verbose&2) {
if(sStimulusName.empty() || sStimulusName == st->name()) {
GetUserInterface().DisplayMessage(
"attach stimulus: %s to node: %s\n",
st->name().c_str(), sn->name().c_str());
}
else {
GetUserInterface().DisplayMessage(
"attach stimulus: %s(%s) to node: %s\n",
sStimulusName.c_str(), st->name().c_str(), sn->name().c_str());
}
}
}
else {
GetUserInterface().DisplayMessage(
"attach warning: %s(%s) not attached to %s\n",
sStimulusName.c_str(), st->name().c_str(), sn->name().c_str());
}
}
stimulus *Pin_t::GetStimulus() {
stimulus_symbol * pPinSymbol = NULL;
if(m_sPin) {
pPinSymbol = dynamic_cast<stimulus_symbol*>(m_sPin);
}
if(m_sPort) {
pPinSymbol = dynamic_cast<stimulus_symbol*>(m_sPort);
}
stimulus * pPin = pPinSymbol == NULL ? NULL : pPinSymbol->getStimulus();
// PinName symbol name only
if(pPin == NULL) {
int iPinNumber = -1;
if(pPinSymbol) {
pPinSymbol->get(iPinNumber);
GetUserInterface().DisplayMessage(
"attach error: pin argument '%s'(%d) type(%s) is not of type Integer or stimulus\n",
pPinSymbol->name().c_str(), iPinNumber,
pPinSymbol->showType().c_str());
}
}
return pPin;
}
IOPIN *Pin_t::GetIOPin() {
bool bSuccess = true;
Module *pMod;
Symbol_Table &ST = get_symbol_table();
if(m_iFlags & Pin_t::eActiveProc) {
pMod = get_active_cpu();
}
else {
// this dynamic_cast always fails here
pMod = dynamic_cast<Module*>(m_sModuleName);
if(pMod == NULL) {
// but the dynamic_cast in findModule() succeeds
pMod = ST.findModule(m_sModuleName->name().c_str());
if(pMod == NULL) {
String *pModName = dynamic_cast<String*>(m_sModuleName);
// but the dynamic_cast in findModule() succeeds
if(pModName != NULL) {
pMod = ST.findModule(*pModName);
}
}
}
}
if(pMod == NULL) {
if (NULL == m_sModuleName) {
GetUserInterface().DisplayMessage(
"attach error: did not find pin '%s'\n",
m_sPin->name().c_str());
}
else {
GetUserInterface().DisplayMessage(
"attach error: did not find module '%s'\n",
m_sModuleName->name().c_str());
}
bSuccess = false;
}
else {
Integer *pPinInt = dynamic_cast<Integer*>(m_sPin);
if(pPinInt != NULL) {
IOPIN *pPinObj = NULL;
if(m_iFlags & Pin_t::ePackageBased) {
// ModName && Integer
// Could be a literal int or a symbol
pPinObj = pMod->get_pin(*pPinInt);
}
else /* if(m_iFlags & Pin_t::ePortBased) */ {
ioport_symbol *pIOPSym = dynamic_cast<ioport_symbol*>(m_sPort);
if(pIOPSym != NULL) {
PortRegister * pPort = pIOPSym->getIOPort();
pPinObj = pPort->getPin(*pPinInt);
}
else {
bSuccess = false;
GetUserInterface().DisplayMessage(
"attach error: did not find port '%s' in module '%s'\n",
m_sPort->name().c_str());
}
}
if(pPinObj != NULL) {
return pPinObj;
// AttachStimulusToNode(sn, pPinInt->name(), pPinObj);
}
else {
bSuccess = false;
GetUserInterface().DisplayMessage(
"attach error: did not find pin '%d' in module '%s'\n",
(int)*pPinInt,
m_sModuleName->name().c_str());
}
}
else {
bSuccess = false;
GetUserInterface().DisplayMessage(
"attach error: pin argument '%s' type(%s) is not of type Integer\n",
m_sPin->name().c_str(),
m_sPin->showType().c_str());
}
}
return (IOPIN*)NULL;
}
Value * Pin_t::GetValue() {
if(m_sPin)
return m_sPin;
if(m_sPort)
return m_sPort;
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1