/* 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 #include #include #include #include #include #include #include #include #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: " <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="<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::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 * 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 :: 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(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(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(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(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(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(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(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(pNode); if(st == NULL) { // Might be an attribute holding a stimulus st = ST.findStimulus(pNode->name()); } Pin_t *pPin = dynamic_cast(*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(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(m_sPin); } if(m_sPort) { pPinSymbol = dynamic_cast(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(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(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(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(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; }