1 /*
2    Copyright (C) 1998 T. Scott Dattalo
3    Copyright (C) 2006,2015 Roy R Rankin
4 
5 This file is part of the libgpsim library of gpsim
6 
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, see
19 <http://www.gnu.org/licenses/lgpl-2.1.html>.
20 */
21 
22 
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <iostream>
26 #include <stdlib.h>
27 #include <string>
28 #include <list>
29 #include <sstream>
30 
31 #include <math.h>
32 #include <string.h>
33 
34 #include "errors.h"
35 #include "gpsim_interface.h"
36 #include "gpsim_object.h"
37 #include "gpsim_time.h"
38 #include "ioports.h"
39 #include "stimuli.h"
40 #include "ui.h"
41 #include "value.h"
42 
43 class Processor;
44 
45 
46 //#define DEBUG
47 #if defined(DEBUG)
48 #define Dprintf(arg) {printf("%s:%d-%s() ",__FILE__,__LINE__,__FUNCTION__); printf arg; }
49 #else
50 #define Dprintf(arg) {}
51 #endif
52 
53 static char num_nodes = 'a';
54 static int num_stimuli = 1;
55 void  gpsim_set_break_delta(guint64 delta, TriggerObject *f = nullptr);
56 
57 extern Processor *active_cpu;
58 /*
59  * stimulus.cc
60  *
61  * This file contains some rudimentary infrastructure to support simulating
62  * the environment outside of the pic. Simple net lists interconnecting pic
63  * I/O pins and various signal generators may be created.
64  *
65  * Details:
66  * There are two basic concepts behind the stimulus code: nodes and stimuli.
67  * The nodes are like wires and the stimuli are like sources and loads. The
68  * nodes define the interconnectivity between the stimuli. In most cases there
69  * will be only two stimuli connected by one node. For example, you may wish
70  * to simulate the effects of a clock input connected to porta.0 . In this case,
71  * the stimuli would be the external clock and the pic I/O pin.
72  */
73 
74 //------------------------------------------------------------------
75 
new_name(const char * cPname,bool)76 void Stimulus_Node::new_name(const char *cPname, bool /* bClearableSymbol */ )
77 {
78     std::cout << " Warning ignoring stimulus node name change from "
79               << name() << " to " << cPname << '\n';
80 }
81 
new_name(std::string & rName,bool bClearableSymbol)82 void Stimulus_Node::new_name(std::string &rName, bool bClearableSymbol)
83 {
84     new_name(rName.c_str(), bClearableSymbol);
85 }
86 
get_nodeVoltage()87 double Stimulus_Node::get_nodeVoltage()
88 {
89     if (future_cycle > cap_start_cycle) // RC calculation in progress, get current value
90         callback();
91     return voltage;
92 }
93 
dump_stimulus_list()94 void dump_stimulus_list()
95 {
96     std::cout << "Stimulus List\n";
97     std::cout << "  implement -- stimuli.cc dump_stimulus_list\n";
98 
99     /*
100     Symbol_Table &ST = get_symbol_table();
101     Symbol_Table::stimulus_symbol_iterator it;
102     Symbol_Table::stimulus_symbol_iterator itEnd = ST.endStimulusSymbol();
103     for(it = ST.beginStimulusSymbol(); it != itEnd; it++) {
104       stimulus *t = (*it)->getStimulus();
105       if(t) {
106         cout << t->name();
107         //if(t->snode)
108         // cout << " attached to " << t->snode->name();
109         t->show();
110         cout << '\n';
111       }
112     }
113     */
114 }
115 
toString()116 std::string Stimulus_Node::toString()
117 {
118     std::string out = name() + " : " + showType();
119 
120     for (stimulus *pt = stimuli; pt; pt = pt->next)
121     {
122         out += "\n\n " + pt->name() + pt->toString();
123     }
124 
125     return out;
126 }
127 
128 //========================================================================
129 
Stimulus_Node(const char * n)130 Stimulus_Node::Stimulus_Node(const char *n)
131     : TriggerObject(nullptr)
132 {
133     warned  = 0;
134     voltage = 0;
135     Cth = 0.0;
136     Zth = 0.0;
137     current_time_constant = 0.0;
138     delta_voltage = 0.0;
139     minThreshold = 0.1; // volts
140     cap_start_cycle = 0;
141     future_cycle = 0;
142     initial_voltage = 0.0;
143     DCVoltage = 0.0;
144     bSettling = false;
145     stimuli = 0;
146     nStimuli = 0;
147     settlingTimeStep = 0;
148 
149     if (n)
150         gpsimObject::new_name(n);
151     else
152     {
153         char name_str[100];
154         snprintf(name_str, sizeof(name_str), "node%d", num_nodes);
155         num_nodes++;    // %%% FIX ME %%%
156         gpsimObject::new_name(name_str);
157     }
158 
159     globalSymbolTable().addSymbol(this);
160 
161     gi.node_configuration_changed(this);
162 }
163 
~Stimulus_Node()164 Stimulus_Node::~Stimulus_Node()
165 {
166     //cout << "~Stimulus_Node\n";
167     stimulus *sptr = stimuli;
168 
169     while (sptr)
170     {
171         sptr->detach(this);
172         sptr = sptr->next;
173     }
174 
175     globalSymbolTable().removeSymbol(this);
176 }
177 
construct(const char * psName)178 Stimulus_Node * Stimulus_Node::construct(const char * psName)
179 {
180     gpsimObject *psn = globalSymbolTable().find(psName);
181 
182     //cout << "constructing stimulus node " << psName << endl;
183     if (psn)
184     {
185         std::cout << "Warning ignoring node creation. A symbol with the name `"
186                   << psName << "' is already in the sybmol table.\n";
187         return nullptr;
188     }
189 
190     return new Stimulus_Node(psName);
191 }
192 
193 //
194 // Add the stimulus 's' to the stimulus list for this node
195 //
196 
attach_stimulus(stimulus * s)197 void Stimulus_Node::attach_stimulus(stimulus *s)
198 {
199     if (!s)
200         return;
201 
202     stimulus *sptr;
203 
204     warned = 0;
205 
206     if (stimuli)
207     {
208         sptr = stimuli;
209         bool searching = 1;
210         int nTotalStimuliConnected = 1;
211 
212         while (searching)
213         {
214             if (s == sptr)
215                 return;      // The stimulus is already attached to this node.
216 
217             nTotalStimuliConnected++;
218             if (sptr->next == 0)
219             {
220                 sptr->next = s;
221                 // s->next = 0;  This is done below
222                 searching = 0;
223             }
224             sptr = sptr->next;
225         }
226 
227         nStimuli = nTotalStimuliConnected;
228     }
229     else
230     {
231         stimuli = s;     // This is the first stimulus attached to this node.
232         nStimuli = 1;
233     }
234 
235     // If we reach this point, then it means that the stimulus that we're
236     // trying to attach has just been placed at the end of the the stimulus
237     // list for this node. So we need to 0 terminate the singly-linked list.
238 
239     s->next = 0;
240 
241     // Now tell the stimulus to attach itself to the node too
242     // (If it hasn't already.)
243 
244     s->attach(this);
245 
246     gi.node_configuration_changed(this);
247 }
248 
249 //
250 // Search for the stimulus 's' in the stimulus list for this node.
251 // If it is found, then remove it from the list.
252 //
253 
detach_stimulus(stimulus * s)254 void Stimulus_Node::detach_stimulus(stimulus *s)
255 {
256     stimulus *sptr;
257 
258     if (!s)          // You can't remove a non-existant stimulus
259         return;
260 
261     if (stimuli)
262     {
263         if (s == stimuli)
264         {
265             // This was the first stimulus in the list.
266 
267             stimuli = s->next;
268             s->detach(this);
269             nStimuli--;
270 
271         }
272         else
273         {
274 
275             sptr = stimuli;
276 
277             do
278             {
279                 if (s == sptr->next)
280                 {
281                     sptr->next = s->next;
282                     s->detach(this);
283                     nStimuli--;
284                     //gi.node_configuration_changed(this);
285                     return;
286                 }
287 
288                 sptr = sptr->next;
289             }
290             while (sptr);
291         }
292     }
293 }
294 
295 //------------------------------------------------------------------------
296 //
297 // Stimulus_Node::update(guint64 current_time)
298 //
299 // update() is called whenever a stimulus attached to this node changes
300 // states.
301 
update(guint64)302 void Stimulus_Node::update(guint64 /* current_time */ )
303 {
304     // So far, 'update' only applies to the current time.
305     update();
306 }
307 
308 //------------------------------------------------------------------------
309 // refresh() - compute the Thevenin voltage and Thevenin impedance
310 //
refresh()311 void Stimulus_Node::refresh()
312 {
313     if (stimuli)
314     {
315 
316         stimulus *sptr = stimuli;
317 
318         initial_voltage = get_nodeVoltage();
319 
320         switch (nStimuli)
321         {
322 
323         case 0:
324             // hmm, strange nStimuli is 0, but the stimuli pointer is non null.
325             break;
326 
327         case 1:
328             // Only one stimulus is attached.
329             DCVoltage = sptr->get_Vth();   // RP - was just voltage
330             Zth =  sptr->get_Zth();
331             break;
332 
333         case 2:
334             // 2 stimuli are attached to the node. This is the typical case
335             // and we'll optimize for it.
336         {
337             stimulus *sptr2 = sptr ? sptr->next : nullptr;
338             if (!sptr2)
339                 break;     // error, nStimuli is two, but there aren't two stimuli
340 
341             double V1, Z1, C1;
342             double V2, Z2, C2;
343             sptr->getThevenin(V1, Z1, C1);
344             sptr2->getThevenin(V2, Z2, C2);
345             DCVoltage = (V1 * Z2  + V2 * Z1) / (Z1 + Z2);
346             Zth = Z1 * Z2 / (Z1 + Z2);
347             Cth = C1 + C2;
348 
349         }
350         break;
351 
352         default:
353         {
354             /*
355               There are 3 or more stimuli connected to this node. Recall
356               that these are all in parallel. The Thevenin voltage and
357               impedance for this is:
358 
359               Thevenin impedance:
360               Zt = 1 / sum(1/Zi)
361 
362               Thevenin voltage:
363 
364               Vt = sum( Vi / ( ((Zi - Zt)/Zt) + 1) )
365               = sum( Vi * Zt /Zi)
366               = Zt * sum(Vi/Zi)
367             */
368 
369             double conductance = 0.0;	// Thevenin conductance.
370             Cth = 0;
371             DCVoltage = 0.0;
372 
373             //cout << "multi-node summing:\n";
374             while (sptr)
375             {
376 
377                 double V1, Z1, C1;
378                 sptr->getThevenin(V1, Z1, C1);
379                 /*
380                 cout << " N: " <<sptr->name()
381                 << " V=" << V1
382                 << " Z=" << Z1
383                 << " C=" << C1 << endl;
384                 */
385 
386                 double Cs = 1 / Z1;
387                 DCVoltage += V1 * Cs;
388                 conductance += Cs;
389                 Cth += C1;
390                 sptr = sptr->next;
391             }
392             Zth = 1.0 / conductance;
393             DCVoltage *= Zth;
394         }
395         }
396 
397         current_time_constant = Cth * Zth;
398         Dprintf(("%s DCVoltage %.3f voltage %.3f Cth=%.2e Zth=%2e time_constant %fsec or %" PRINTF_GINT64_MODIFIER "d cycles now=%" PRINTF_GINT64_MODIFIER "d \n",name().c_str(), DCVoltage, voltage, Cth, Zth, current_time_constant, (guint64)(current_time_constant*get_cycles().instruction_cps()), get_cycles().get()));
399         if (((guint64)(current_time_constant*get_cycles().instruction_cps()) < 5) ||
400                 (fabs(DCVoltage - voltage) < minThreshold))
401         {
402             if (verbose)
403                 std::cout << "Stimulus_Node::refresh " << name() << " use DC " <<
404                           DCVoltage << " as current_time_constant=" <<
405                           current_time_constant << '\n';
406 
407             if (future_cycle)	// callback is active
408             {
409                 get_cycles().clear_break(this);
410             }
411 
412             voltage = DCVoltage;
413             future_cycle = 0;
414 
415         }
416         else
417         {
418             settlingTimeStep = calc_settlingTimeStep();
419             voltage = initial_voltage;
420 
421             if (verbose)
422                 std::cout << "Stimulus_Node::refresh " << name() << " settlingTimeStep="
423                           << settlingTimeStep << " voltage=" << voltage << " Finalvoltage="
424                           << DCVoltage << '\n';
425             /*
426             If future_cycle is not 0 we are in the middle of an RC
427             calculation, but an input condition has changed.
428             */
429 
430             if (future_cycle && (get_cycles().get() > cap_start_cycle))
431             {
432                 callback();
433             }
434             else
435             {
436                 if (future_cycle) get_cycles().clear_break(this);
437                 cap_start_cycle = get_cycles().get();
438                 future_cycle = cap_start_cycle + settlingTimeStep;
439                 get_cycles().set_break(future_cycle,this);
440 #ifdef DEBUG
441                 get_cycles().dump_breakpoints();
442 #endif
443             }
444         }
445 
446     }
447 }
448 
calc_settlingTimeStep()449 guint64 Stimulus_Node::calc_settlingTimeStep()
450 {
451     /* Select a time interval where the voltage does not change more
452        than about 0.125 volts in each step(unless timestep < 1).
453        First we calculate dt_dv = CR/V with dt in cpu cycles to
454        determine settling time step
455     */
456     guint64 TimeStep;
457     double dv = fabs(DCVoltage - voltage);
458 
459     // avoid divide by zero
460     if (dv < 0.000001)
461         dv = 0.000001;
462 
463     double dt_dv = get_cycles().instruction_cps() * current_time_constant / dv;
464     TimeStep = (guint64) (0.125 * dt_dv);
465     TimeStep = (TimeStep) ? TimeStep : 1;
466 
467     Dprintf(("%s dt_dv = %.2f TimeStep 0x%" PRINTF_GINT64_MODIFIER "x now 0x%" PRINTF_GINT64_MODIFIER "x\n", __FUNCTION__, dt_dv, TimeStep, get_cycles().get()));
468 
469     return TimeStep;
470 }
471 
472 //------------------------------------------------------------------------
473 // updateStimuli
474 //
475 // drive all the stimuli connected to this node.
476 
updateStimuli()477 void Stimulus_Node::updateStimuli()
478 {
479     stimulus *sptr = stimuli;
480 
481     while (sptr)
482     {
483         sptr->set_nodeVoltage(voltage);
484         sptr = sptr->next;
485     }
486 }
487 
update()488 void Stimulus_Node::update()
489 {
490     if (stimuli)
491     {
492 
493         refresh();
494         updateStimuli();
495     }
496 }
497 
set_nodeVoltage(double v)498 void Stimulus_Node::set_nodeVoltage(double v)
499 {
500     voltage = v;
501     updateStimuli();
502 }
503 
504 //------------------------------------------------------------------------
callback()505 void Stimulus_Node::callback()
506 {
507     if (verbose)
508         callback_print();
509 
510     initial_voltage = voltage;
511     double Time_Step;
512     double expz;
513     //
514     // increase time step as capacitor charges more slowly as final
515     // voltage is approached.
516     //
517 
518     //
519     // The following is an exact calculation, assuming no circuit
520     // changes,  regardless of time step.
521     //
522     Time_Step = (get_cycles().get() - cap_start_cycle) /
523                 (get_cycles().instruction_cps() * current_time_constant);
524     expz = exp(-Time_Step);
525     voltage = DCVoltage - (DCVoltage - voltage) * expz;
526 
527     if (verbose)
528         std::cout << "\tVoltage was " << initial_voltage << "V now "
529                   << voltage << "V\n";
530 
531     if (fabs(DCVoltage - voltage) < minThreshold)
532     {
533         voltage = DCVoltage;
534         if (future_cycle)
535             get_cycles().clear_break(this);
536         future_cycle = 0;
537         if (verbose)
538             std::cout << "\t" << name() <<
539                       " Final voltage " << DCVoltage << " reached at "
540                       << get_cycles().get() << " cycles\n";
541         Dprintf(("%s DC Voltage %.2f reached at 0x%" PRINTF_GINT64_MODIFIER "x cycles\n", name().c_str(), DCVoltage, get_cycles().get()));
542     }
543     else if(get_cycles().get() >= future_cycle) // got here via break
544     {
545         settlingTimeStep = calc_settlingTimeStep();
546         cap_start_cycle = get_cycles().get();
547         get_cycles().clear_break(this);
548         future_cycle = cap_start_cycle + settlingTimeStep;
549         get_cycles().set_break(future_cycle, this);
550 #ifdef DEBUG
551         get_cycles().dump_breakpoints();
552 #endif
553         if (verbose)
554             std::cout << "\tBreak reached at " << cap_start_cycle <<
555                       " cycles, next break set for "
556                       << future_cycle << " delta=" << settlingTimeStep << '\n';
557     }
558     else	// updating value before break don't increase step size
559     {
560         cap_start_cycle = get_cycles().get();
561         get_cycles().reassign_break(future_cycle,
562                                     cap_start_cycle + settlingTimeStep, this);
563         future_cycle = get_cycles().get() + settlingTimeStep;
564         if (verbose)
565             std::cout << "\tcallback called at " << cap_start_cycle <<
566                       " cycles, next break set for " << future_cycle << " delta="
567                       << settlingTimeStep << '\n';
568     }
569 
570     updateStimuli();
571 }
572 
573 //------------------------------------------------------------------------
callback_print()574 void Stimulus_Node::callback_print()
575 {
576     std::cout << "Node: " << name();
577     TriggerObject::callback_print();
578 }
579 
580 //------------------------------------------------------------------------
stimulus(const char * cPname,double _Vth,double _Zth)581 stimulus::stimulus(const char *cPname, double _Vth, double _Zth)
582     : Value(cPname, "", nullptr), snode(nullptr), next(nullptr),
583       bDrivingState(false), bDriving(false),
584       Vth(_Vth), Zth(_Zth),
585       Cth(0.0), // Farads
586       nodeVoltage(0.0) // volts
587 {
588 }
589 
new_name(const char * cPname,bool)590 void stimulus::new_name(const char *cPname, bool /* bClearableSymbol */ )
591 {
592     globalSymbolTable().removeSymbol(this);
593     gpsimObject::new_name(cPname);
594     globalSymbolTable().addSymbol(this);
595 
596     stimulus *psn = dynamic_cast<stimulus *>(globalSymbolTable().find(name()));
597     if (psn)
598     {
599         if (psn == this)
600             ; //cout << "Successfully added " << name() << " to symbol table\n";
601         else
602             std::cout << "Successfully added " << name() << " but it's not equal to this node\n";
603     }
604     else
605         std::cout << "Failed to add " << name() << " to symbol table\n";
606 
607     /*
608     const char *cPoldName = name().c_str();
609     if(name_str.empty() && cPname != NULL && *cPname != 0) {
610       // Assume never in symbol table.
611       // Every named stimulus goes into the symbol table.
612       gpsimObject::new_name(cPname);
613       symbol_table.add_stimulus(this,bClearableSymbol);
614       return;
615     }
616     if(symbol_table.Exist(cPoldName)) {
617       // The symbol is in the symbol table. Since the
618       // symbol table is ordered we need to let the
619       // symbol table rename the object to maintain
620       // ordering. Yuk.
621       // Note that rename() will call stimulus::new_name()
622       // after the symbol is removed. This recursive
623       // call will then enter the branch that calls
624       // gpsimObject::new_name(). The simulus with
625       // its new name is added into the symbol table.
626       symbol_table.rename(cPoldName,cPname);
627     }
628     else {
629       gpsimObject::new_name(cPname);
630     }
631     */
632 }
633 
new_name(std::string & rName,bool bClearableSymbol)634 void stimulus::new_name(std::string &rName, bool bClearableSymbol)
635 {
636     new_name(rName.c_str(), bClearableSymbol);
637 }
638 
~stimulus()639 stimulus::~stimulus()
640 {
641     if (snode)
642         snode->detach_stimulus(this);
643 
644     globalSymbolTable().removeSymbol(this);
645     //cout << "Removing " << name() << " from ST\n";
646 }
647 
show()648 void stimulus::show()
649 {
650     GetUserInterface().DisplayMessage(toString().c_str());
651 }
652 
toString()653 std::string stimulus::toString()
654 {
655     std::ostringstream s;
656 
657     s << " stimulus ";
658     if (snode)
659         s << " attached to " << snode->name();
660 
661 
662     s << '\n'
663       << " Vth=" << get_Vth() << "V"
664       << " Zth=" << get_Zth() << " ohms"
665       << " Cth=" << get_Cth() << "F"
666       << " nodeVoltage= " << get_nodeVoltage() << "V"
667       << '\n'
668       << " Driving=" << (getDriving()?"OUT":"IN")
669       << " drivingState=" << getDrivingState()
670       << " drivenState=" << getDrivenState()
671       << " bitState=" << getBitChar();
672 
673     return s.str();
674 }
675 
attach(Stimulus_Node * s)676 void stimulus::attach(Stimulus_Node *s)
677 {
678     detach(snode);
679     snode = s;
680 }
681 
detach(Stimulus_Node * s)682 void stimulus::detach(Stimulus_Node *s)
683 {
684     if (snode == s)
685         snode = nullptr;
686 }
687 
getThevenin(double & v,double & z,double & c)688 void stimulus::getThevenin(double &v, double &z, double &c)
689 {
690     v = get_Vth();
691     z = get_Zth();
692     c = get_Cth();
693 }
694 
695 //========================================================================
696 //
PinMonitor()697 PinMonitor::PinMonitor()
698 {
699 }
700 
~PinMonitor()701 PinMonitor::~PinMonitor()
702 {
703     // Release all of the sinks:
704     for (SignalSink *ssi : sinks)
705     {
706         Dprintf(("release sink %p\n", ssi));
707         fflush(stdout);
708         (*ssi).release();
709     }
710 
711     for (AnalogSink *asi : analogSinks)
712     {
713         (*asi).release();
714     }
715 }
716 
addSink(SignalSink * new_sink)717 void PinMonitor::addSink(SignalSink *new_sink)
718 {
719     if (new_sink)
720     {
721         //cout << "Adding sink: " << new_sink << endl;
722         sinks.push_back(new_sink);
723     }
724 }
725 
removeSink(SignalSink * pSink)726 void PinMonitor::removeSink(SignalSink *pSink)
727 {
728     if (pSink)
729     {
730         sinks.remove(pSink);
731     }
732 }
733 
addSink(AnalogSink * new_sink)734 void PinMonitor::addSink(AnalogSink *new_sink)
735 {
736     if (new_sink)
737         analogSinks.push_back(new_sink);
738 }
739 
removeSink(AnalogSink * pSink)740 void PinMonitor::removeSink(AnalogSink *pSink)
741 {
742     if (pSink)
743         analogSinks.remove(pSink);
744 }
745 //========================================================================
746 
747 
square_wave(unsigned int p,unsigned int dc,unsigned int ph,const char * n)748 square_wave::square_wave(unsigned int p, unsigned int dc, unsigned int ph, const char *n)
749 {
750     //cout << "creating sqw stimulus\n";
751 
752     if (n)
753         new_name(n, false);
754     else
755     {
756         char name_str[100];
757         snprintf(name_str, sizeof(name_str), "s%d_square_wave", num_stimuli);
758         num_stimuli++;
759         new_name(name_str, false);
760     }
761 
762     period = p;   // cycles
763     duty   = dc;  // # of cycles over the period for which the sq wave is high
764     phase  = ph;  // phase of the sq wave wrt the cycle counter
765     time   = 0;   // simulation time
766     snode = 0;
767     next = 0;
768 }
769 
get_Vth()770 double square_wave::get_Vth()
771 {
772     guint64 current_time = get_cycles().get();
773 
774     if (verbose & 1)
775         std::cout << "Getting new state of the square wave.\n";
776 
777     if (((current_time+phase) % period) <= duty)
778         return Vth;
779     else
780         return 0.0;
781 }
782 
783 
784 //========================================================================
785 //
786 // triangle_wave
787 
triangle_wave(unsigned int p,unsigned int dc,unsigned int ph,const char * n)788 triangle_wave::triangle_wave(unsigned int p, unsigned int dc, unsigned int ph, const char *n)
789 {
790     //cout << "creating sqw stimulus\n";
791 
792     if (n)
793         new_name(n,false);
794     else
795     {
796         char name_str[100];
797         snprintf(name_str, sizeof(name_str), "s%d_triangle_wave", num_stimuli);
798         num_stimuli++;
799         new_name(name_str,false);
800     }
801 
802     if (p == 0)  //error
803         p = 1;
804 
805     // copy the square wave stuff
806     period = p;   // cycles
807     duty   = dc;  // # of cycles over the period for which the sq wave is high
808     phase  = ph;  // phase of the sq wave wrt the cycle counter
809     time   = 0;   // simulation time
810     snode = 0;
811     next = 0;
812 
813     //cout << "duty cycle " << dc << " period " << p << " drive " << drive << '\n';
814 
815     // calculate the slope and the intercept for the two lines comprising
816     // the triangle wave:
817 
818     if (duty)
819         m1 = Vth / duty;
820     else
821         m1 = Vth / period;   // m1 will not be used if the duty cycle is zero
822 
823     b1 = 0;
824 
825     if (period != duty)
826         m2 = Vth / (duty - period);
827     else
828         m2 = Vth;
829 
830     b2 = -m2 * period;
831 
832     //cout << "m1 = " << m1 << " b1 = " << b1 << '\n';
833     //cout << "m2 = " << m2 << " b2 = " << b2 << '\n';
834 }
835 
get_Vth()836 double triangle_wave::get_Vth()
837 {
838     guint64 current_time = get_cycles().get();
839 
840     //cout << "Getting new state of the triangle wave.\n";
841 
842     guint64 t = (current_time + phase) % period;
843 
844     double ret_val;
845 
846     if (t <= duty)
847         ret_val = b1 + m1 * t;
848     else
849         ret_val = b2 + m2 * t;
850 
851     //  cout << "Triangle wave: t = " << t << " value = " << ret_val << '\n';
852     return ret_val;
853 }
854 
855 //========================================================================
856 //
857 // Event
858 
Event()859 Event::Event()
860 {
861     current_state = 0;
862 }
863 
864 //========================================================================
865 //
callback()866 void Event::callback()
867 {
868     // If there's a node attached to this stimulus, then update it.
869     if (snode)
870         snode->update();
871 
872     // If the event is inactive.
873 
874     if (current_state == 0)
875     {
876         get_cycles().set_break_delta(1, this);
877         current_state = 1;
878     }
879     else
880     {
881         current_state = 0;
882     }
883 }
884 
callback_print()885 void source_stimulus::callback_print()
886 {
887     std::cout << "stimulus " << name() << " CallBack ID " << CallBackID << '\n';
888 }
889 
890 
callback()891 void source_stimulus::callback()
892 {
893     std::cout << "shouldn't be called\n";
894 }
895 
show()896 void source_stimulus::show()
897 {
898     stimulus::show();
899 }
900 
put_period(Value * pValue)901 void source_stimulus::put_period(Value *pValue)
902 {
903     if (pValue)
904         pValue->get(period);
905 }
906 
put_duty(Value * pValue)907 void source_stimulus::put_duty(Value *pValue)
908 {
909     if (pValue)
910         pValue->get(duty);
911 }
912 
put_phase(Value * pValue)913 void source_stimulus::put_phase(Value *pValue)
914 {
915     if (pValue)
916         pValue->get(phase);
917 }
918 
put_initial_state(Value * pValue)919 void source_stimulus::put_initial_state(Value *pValue)
920 {
921     if (pValue)
922         pValue->get(initial_state);
923 }
924 
put_start_cycle(Value * pValue)925 void source_stimulus::put_start_cycle(Value *pValue)
926 {
927     if (pValue)
928         pValue->get(start_cycle);
929 }
930 
931 //========================================================================
932 //
IOPIN(const char * _name,double _Vth,double _Zth,double _ZthWeak,double _ZthFloating)933 IOPIN::IOPIN(const char *_name,
934              double _Vth,
935              double _Zth,
936              double _ZthWeak,
937              double _ZthFloating
938             )
939 
940     : stimulus(_name, _Vth, _Zth),
941       gui_name_updated(false),
942       bDrivenState(false),
943       cForcedDrivenState('Z'),
944       m_monitor(nullptr),
945       ZthWeak(_ZthWeak), ZthFloating(_ZthFloating),
946       l2h_threshold(2.0),       // PICs are CMOS and use CMOS-like thresholds
947       h2l_threshold(1.0),
948       Vdrive_high(4.4),
949       Vdrive_low(0.6),
950       schmitt_level(false)
951 {
952     if (verbose)
953         std::cout << "IOPIN default constructor\n";
954     is_analog = false;
955 }
956 
set_digital_threshold(double vdd)957 void IOPIN::set_digital_threshold(double vdd)
958 {
959     if (schmitt_level)
960     {
961         set_l2h_threshold(0.8*vdd);
962         set_h2l_threshold(0.2*vdd);
963     }
964     else
965     {
966         set_l2h_threshold(vdd > 4.5 ? 2.0 : 0.25 * vdd + 0.8);
967         set_h2l_threshold(vdd > 4.5 ? 0.8 : 0.15 * vdd);
968     }
969     Vdrive_high = vdd - 0.6;
970     Vdrive_low = 0.6;
971 }
972 
setMonitor(PinMonitor * new_pinMonitor)973 void IOPIN::setMonitor(PinMonitor *new_pinMonitor)
974 {
975     if (m_monitor && new_pinMonitor)
976         std::cout << "IOPIN already has a monitor!\n";
977     else
978         m_monitor = new_pinMonitor;
979 }
980 // True = set schmitt trigger levels, False = set TTL levels
set_schmitt_level(bool _schmitt,double vdd)981 void IOPIN::set_schmitt_level(bool _schmitt, double vdd)
982 {
983 
984     if (_schmitt != schmitt_level)
985     {
986         schmitt_level = _schmitt;
987         set_digital_threshold(vdd);
988     }
989 }
990 
~IOPIN()991 IOPIN::~IOPIN()
992 {
993     if (m_monitor)
994         ((PinModule *)m_monitor)->clrPin();
995 }
996 
get(char * return_str,int len)997 void IOPIN::get(char *return_str, int len)
998 {
999     if (return_str)
1000     {
1001         if (get_direction() == DIR_OUTPUT)
1002             strncpy(return_str, IOPIN::getDrivingState() ? "1" : "0", len);
1003         else
1004             strncpy(return_str, IOPIN::getState() ? "1" : "0", len);
1005     }
1006 }
1007 
attach(Stimulus_Node * s)1008 void IOPIN::attach(Stimulus_Node *s)
1009 {
1010     snode = s;
1011 }
1012 
show()1013 void IOPIN::show()
1014 {
1015     stimulus::show();
1016 }
1017 
1018 /*
1019     This is required in this class so get_Vth(), get_Zth() and get_Cth()
1020     in this class are used to compute Thevenin voltage and Thevenin impedance
1021 */
1022 
getThevenin(double & v,double & z,double & c)1023 void IOPIN::getThevenin(double &v, double &z, double &c)
1024 {
1025     v = get_Vth();
1026     z = get_Zth();
1027     c = get_Cth();
1028 }
1029 //--------------------
1030 // set_nodeVoltage()
1031 //
1032 //
set_nodeVoltage(double new_nodeVoltage)1033 void IOPIN::set_nodeVoltage(double new_nodeVoltage)
1034 {
1035     if (verbose & 1)
1036         std::cout << name() << " set_nodeVoltage old=" << nodeVoltage << " new=" << new_nodeVoltage << '\n';
1037 
1038     nodeVoltage = new_nodeVoltage;
1039 
1040     if (nodeVoltage < h2l_threshold)
1041     {
1042         // The voltage is below the low threshold
1043         setDrivenState(false);
1044     }
1045     else if (nodeVoltage > l2h_threshold)
1046     {
1047 
1048         // The voltage is above the high threshold
1049         setDrivenState(true);
1050 
1051     }
1052     else
1053     {
1054         // The voltage is between the low and high thresholds,
1055         // so do nothing
1056     }
1057 
1058     //setDrivenState(getBitChar());
1059     if (m_monitor)
1060         m_monitor->set_nodeVoltage(nodeVoltage);
1061 }
1062 
1063 //------------------------------------------------------------
1064 // putState - called by peripherals when they wish to
1065 // drive an I/O pin to a new state.
1066 
putState(bool new_state)1067 void IOPIN::putState(bool new_state)
1068 {
1069     if (new_state != bDrivingState)
1070     {
1071         bDrivingState = new_state;
1072         Vth = bDrivingState ? Vdrive_high : Vdrive_low;
1073 
1074         if (verbose & 1)
1075             std::cout << name() << " putState= " << (new_state ? "high\n" : "low\n");
1076 
1077         // If this pin is tied to a node, then update the node.
1078         // Note that when the node is updated, then the I/O port
1079         // (if there is one) holding this I/O pin will get updated.
1080         // If this pin is not tied to a node, then try to update
1081         // the I/O port directly.
1082 
1083         if (snode)
1084             snode->update();
1085     }
1086     if (m_monitor)
1087         m_monitor->putState(new_state ? '1' : '0');
1088 }
1089 
putState(double new_Vth)1090 void IOPIN::putState(double new_Vth)
1091 {
1092     if (new_Vth != Vth)
1093     {
1094         Vth = new_Vth;
1095 
1096         if (Vth <= 0.3)
1097             bDrivingState = false;
1098         else
1099             bDrivingState = true;
1100 
1101         if (verbose & 1)
1102             std::cout << name() << " putState=" << new_Vth << '\n';
1103 
1104         // If this pin is tied to a node, then update the node.
1105         if (snode)
1106             snode->update();
1107     }
1108     if (m_monitor)
1109         m_monitor->putState(bDrivingState ? '1' : '0');
1110 }
1111 
1112 //------------------------------------------------------------
getState()1113 bool IOPIN::getState()
1114 {
1115     return getDriving() ? getDrivingState() : getDrivenState();
1116 }
1117 
setDrivingState(bool new_state)1118 void IOPIN::setDrivingState(bool new_state)
1119 {
1120     bDrivingState = new_state;
1121 
1122     if (m_monitor)
1123         m_monitor->setDrivingState(bDrivingState ? '1' : '0');
1124 
1125     if (verbose & 1)
1126         std::cout << name() << " setDrivingState= " << (new_state ? "high\n" : "low\n");
1127 }
1128 
setDrivingState(char new3State)1129 void IOPIN::setDrivingState(char new3State)
1130 {
1131     bDrivingState = (new3State == '1' || new3State == 'W');
1132 
1133     if (m_monitor)
1134         m_monitor->setDrivingState(new3State);
1135 }
1136 
getDrivingState()1137 bool IOPIN::getDrivingState()
1138 {
1139     return bDrivingState;
1140 }
1141 
getDrivenState()1142 bool IOPIN::getDrivenState()
1143 {
1144     return bDrivenState;
1145 }
1146 
1147 //------------------------------------------------------------------------
1148 // setDrivenState
1149 //
1150 // An stimulus attached to this pin is driving us to a new state.
1151 // This state will be recorded and propagate up to anything
1152 // monitoring this pin.
1153 
setDrivenState(bool new_state)1154 void IOPIN::setDrivenState(bool new_state)
1155 {
1156     bDrivenState = new_state;
1157 
1158     if (verbose & 1)
1159         std::cout << name() << " setDrivenState= " << (new_state ? "high\n" : "low\n");
1160 
1161     // Propagate the new state to those things monitoring this pin.
1162     // (note that the 3-state value is what's propagated).
1163     if (m_monitor && !is_analog)
1164     {
1165         m_monitor->setDrivenState(getBitChar());
1166         if (verbose & 16)
1167             std::cout << name() << " setting state of monitor to " << getBitChar() << '\n';
1168     }
1169 }
1170 
1171 //------------------------------------------------------------------------
1172 // forceDrivenState() - allows the 'driven state' to be manipulated whenever
1173 // there is no snode attached. The primary purpose of this is to allow the
1174 // UI to toggle I/O pin states.
1175 //
forceDrivenState(char newForcedState)1176 void IOPIN::forceDrivenState(char newForcedState)
1177 {
1178     if (cForcedDrivenState != newForcedState)
1179     {
1180 
1181         cForcedDrivenState = newForcedState;
1182 
1183         bDrivenState = cForcedDrivenState == '1' || cForcedDrivenState == 'W';
1184 
1185         if (m_monitor)
1186         {
1187             m_monitor->setDrivenState(getBitChar());
1188             m_monitor->updateUI();
1189         }
1190     }
1191 }
1192 
getForcedDrivenState()1193 char IOPIN::getForcedDrivenState()
1194 {
1195     return cForcedDrivenState;
1196 }
1197 
toggle()1198 void IOPIN::toggle()
1199 {
1200     putState((bool) (getState() ^ true));
1201 }
1202 
1203 /*************************************
1204  *  int IOPIN::get_Vth()
1205  *
1206  * If this iopin has a stimulus attached to it then
1207  * the voltage will be dictated by the stimulus. Otherwise,
1208  * the voltage is determined by the state of the ioport register
1209  * that is inside the pic. For an input (like this), the pic code
1210  * that is being simulated can not change the state of the I/O pin.
1211  * However, the user has the ability to modify the state of
1212  * this register either by writing directly to it in the cli,
1213  * or by clicking in one of many places in the gui.
1214  */
get_Vth()1215 double IOPIN::get_Vth()
1216 {
1217     return Vth;
1218 }
1219 
getBitChar()1220 char IOPIN::getBitChar()
1221 {
1222     if (!snode)
1223         return getForcedDrivenState();      // RCP - Changed to match IO_bi_directional
1224 //  was  return 'Z';  // High impedance - unknown state.
1225 
1226     if (snode->get_nodeZth() > ZthFloating)
1227         return 'Z';
1228 
1229     if (snode->get_nodeZth() > ZthWeak)
1230         return getDrivenState() ? 'W' : 'w';
1231 
1232     return getDrivenState() ? '1' : '0';
1233 }
1234 
newGUIname(const char * s)1235 void IOPIN::newGUIname(const char *s)
1236 {
1237     if (s)
1238     {
1239         gui_name_updated = true;
1240         gui_name = s;
1241     }
1242 }
1243 
1244 // FIXME: Should not cast away constness
GUIname() const1245 std::string &IOPIN::GUIname() const
1246 {
1247     return (std::string &)gui_name;
1248 }
1249 
1250 //========================================================================
1251 //
IO_bi_directional(const char * _name,double _Vth,double _Zth,double _ZthWeak,double _ZthFloating,double _VthIn,double _ZthIn)1252 IO_bi_directional::IO_bi_directional(const char *_name,
1253                                      double _Vth,
1254                                      double _Zth,
1255                                      double _ZthWeak,
1256                                      double _ZthFloating,
1257                                      double _VthIn,
1258                                      double _ZthIn)
1259     : IOPIN(_name, _Vth, _Zth, _ZthWeak, _ZthFloating),
1260       ZthIn(_ZthIn), VthIn(_VthIn)
1261 {
1262 }
1263 
1264 /*
1265     This is required in this class so get_Vth(), get_Zth() and get_Cth()
1266     in this class are used to compute Thevenin voltage and Thevenin impedance
1267 */
1268 
getThevenin(double & v,double & z,double & c)1269 void IO_bi_directional::getThevenin(double &v, double &z, double &c)
1270 {
1271     v = get_Vth();
1272     z = get_Zth();
1273     c = get_Cth();
1274 }
set_nodeVoltage(double new_nodeVoltage)1275 void IO_bi_directional::set_nodeVoltage(double new_nodeVoltage)
1276 {
1277     IOPIN::set_nodeVoltage(new_nodeVoltage);
1278 }
1279 
get_Vth()1280 double IO_bi_directional::get_Vth()
1281 {
1282     if (getDriving())
1283         return getDrivingState() ? Vth : 0.0;
1284 
1285 
1286     //return getDrivingState() ? VthIn : 0;
1287     return VthIn;
1288 }
1289 
get_Zth()1290 double IO_bi_directional::get_Zth()
1291 {
1292     return getDriving() ? Zth : ZthIn;
1293 }
1294 
1295 /*
1296    getBitChar() returns bit status as follows
1297      Input pin
1298 	1> Pin considered floating,
1299 	   return 'Z'
1300 	2> Weak Impedance on pin,
1301 	   return 'W" if high or 'w' if low
1302 	3> Pin being driven externally
1303 	   return '1' node voltage high '0' if low
1304      Output pin
1305 	1> Node voltage opposite driven value
1306 	   return 'X' if node voltage high or 'x' if inode voltage low
1307 	2> Node voltage same as driven value
1308 	   return '1' node voltage high '0' if low
1309 */
1310 
getBitChar()1311 char IO_bi_directional::getBitChar()
1312 {
1313     if (!snode && !getDriving())
1314         return getForcedDrivenState();
1315 
1316     if (snode)
1317     {
1318         if (!getDriving())		// input pin
1319         {
1320             if (snode->get_nodeZth() > ZthFloating)
1321                 return 'Z';
1322 
1323             if (snode->get_nodeZth() > ZthWeak)
1324                 return getDrivenState() ? 'W' : 'w';
1325         }
1326         else if (getDrivenState() != getDrivingState())
1327             return getDrivenState() ? 'X' : 'x';
1328     }
1329 
1330     return getDrivenState() ? '1' : '0';
1331 }
1332 
1333 
1334 //---------------
1335 //::update_direction(unsigned int new_direction)
1336 //
1337 //  This is called when a new value is written to the tris register
1338 // with which this bi-direction pin is associated.
1339 
update_direction(unsigned int new_direction,bool refresh)1340 void IO_bi_directional::update_direction(unsigned int new_direction, bool refresh)
1341 {
1342     setDriving(new_direction ? true : false);
1343 
1344     // If this pin is not associated with an IO Port, but it's tied
1345     // to a stimulus, then we need to update the stimulus.
1346     if (refresh && snode)
1347         snode->update();
1348 }
1349 
putState(bool new_state)1350 void IO_bi_directional::putState(bool new_state)
1351 {
1352     IOPIN::putState(new_state);
1353 }
1354 
putState(double new_Vth)1355 void IO_bi_directional::putState(double new_Vth)
1356 {
1357     VthIn = new_Vth;
1358     IOPIN::putState(new_Vth);
1359 }
1360 
IO_bi_directional_pu(const char * _name,double _Vth,double _Zth,double _ZthWeak,double _ZthFloating,double _VthIn,double _ZthIn,double _Zpullup)1361 IO_bi_directional_pu::IO_bi_directional_pu(const char *_name,
1362         double _Vth,
1363         double _Zth,
1364         double _ZthWeak,
1365         double _ZthFloating,
1366         double _VthIn,
1367         double _ZthIn,
1368         double _Zpullup)
1369     : IO_bi_directional(_name, _Vth, _Zth, _ZthWeak,
1370                         _ZthFloating, _VthIn, _ZthIn),
1371       Zpullup(_Zpullup)
1372 {
1373     Vpullup = Vth;
1374     bPullUp = false;
1375 }
1376 
~IO_bi_directional_pu()1377 IO_bi_directional_pu::~IO_bi_directional_pu()
1378 {
1379 }
1380 
1381 /*
1382     This is required in this class so get_Vth(), get_Zth() and get_Cth()
1383     in this class are used to compute Thevenin voltage and Thevenin impedance
1384 */
1385 
getThevenin(double & v,double & z,double & c)1386 void IO_bi_directional_pu::getThevenin(double &v, double &z, double &c)
1387 {
1388     v = get_Vth();
1389     z = get_Zth();
1390     c = get_Cth();
1391 }
set_is_analog(bool flag)1392 void IO_bi_directional_pu::set_is_analog(bool flag)
1393 {
1394     if (is_analog != flag)
1395     {
1396         is_analog = flag;
1397 
1398         if (snode)
1399             snode->update();
1400         else if (!getDriving())
1401             setDrivenState(bPullUp && ! is_analog);
1402     }
1403 }
1404 
update_pullup(char new_state,bool refresh)1405 void IO_bi_directional_pu::update_pullup(char new_state, bool refresh)
1406 {
1407     bool bNewPullupState = new_state == '1' || new_state == 'W';
1408     if (bPullUp != bNewPullupState)
1409     {
1410         bPullUp = bNewPullupState;
1411         if (refresh)
1412         {
1413             // If there is a node attached to the pin, then we already
1414             // know the driven state. If there is no node attached and
1415             // this pin is configured as an input, then let the drivenState
1416             // be the same as the pullup state.
1417             if (snode)
1418                 snode->update();
1419             else if (!getDriving())
1420                 setDrivenState(bPullUp && ! is_analog);
1421         }
1422     }
1423 }
1424 
get_Zth()1425 double IO_bi_directional_pu::get_Zth()
1426 {
1427     return getDriving() ? Zth : ((bPullUp && ! is_analog) ? Zpullup : ZthIn);
1428 }
1429 
get_Vth()1430 double IO_bi_directional_pu::get_Vth()
1431 {
1432     /**/
1433     if (verbose & 1)
1434         std::cout << " " << name() << " get_Vth PU "
1435                   << " Direction=" << (getDriving()?"OUT":"IN")
1436                   << " DrivingState=" << getDrivingState()
1437                   << " bDrivenState=" << bDrivenState
1438                   << " Vth=" << Vth
1439                   << " VthIn=" << VthIn
1440                   << " bPullUp=" << bPullUp
1441                   << " is_analog=" << is_analog << '\n';
1442     /**/
1443 
1444     // If the pin is configured as an output, then the driving voltage
1445     // depends on the pin state. If the pin is an input, and the pullup resistor
1446     // is enabled, then the pull-up resistor will 'drive' the output. The
1447     // open circuit voltage in this case will be Vth (the thevenin voltage,
1448     // which is assigned to be same as the processor's supply voltage).
1449 
1450     if (getDriving())
1451         return getDrivingState() ? Vth : 0.0;
1452     else
1453         return (bPullUp && ! is_analog) ? Vpullup : VthIn;
1454 }
1455 
1456 /*
1457    getBitChar() returns bit status as follows
1458      Input pin
1459 	1> Pin considered floating,
1460 	   return 'Z'
1461 	2> Weak Impedance on pin,
1462 	   return 'W" if high or 'w' if low
1463 	3> Pin being driven externally
1464 	   return '1' node voltage high '0' if low
1465      Output pin
1466 	1> Node voltage opposite driven value
1467 	   return 'X' if node voltage high or 'x' if inode voltage low
1468 	2> Node voltage same as driven value
1469 	   return '1' node voltage high '0' if low
1470 */
1471 
getBitChar()1472 char IO_bi_directional_pu::getBitChar()
1473 {
1474     if (!snode && !getDriving() )
1475     {
1476         char cForced=getForcedDrivenState();
1477         return (cForced=='Z' && bPullUp) ? 'W' : cForced;
1478     }
1479 
1480     if (snode)
1481     {
1482 
1483         if (!getDriving())		// input pin
1484         {
1485             if (snode->get_nodeZth() > ZthFloating)
1486                 return 'Z';
1487 
1488             if (snode->get_nodeZth() > ZthWeak)
1489                 return getDrivenState() ? 'W' : 'w';
1490         }
1491         else if (getDrivenState() != getDrivingState())
1492             return getDrivenState() ? 'X' : 'x';
1493     }
1494 
1495     return getDrivenState() ? '1' : '0';
1496 }
1497 /*
1498    This class adds open_collector(drain) pin functionality.
1499    when OpenDrain is true(default) the pin does not source current then output is high
1500    ODCON register sets or clears the OpenDrain flag
1501 */
IO_open_collector(const char * _name)1502 IO_open_collector::IO_open_collector(const char *_name)
1503     : IO_bi_directional_pu(_name), OpenDrain(true)
1504 {
1505 }
1506 
1507 /*
1508     This is required in this class so get_Vth(), get_Zth() and get_Cth()
1509     in this class are used to compute Thevenin voltage and Thevenin impedance
1510 */
1511 
getThevenin(double & v,double & z,double & c)1512 void IO_open_collector::getThevenin(double &v, double &z, double &c)
1513 {
1514     v = get_Vth();
1515     z = get_Zth();
1516     c = get_Cth();
1517 }
1518 
get_Vth()1519 double IO_open_collector::get_Vth()
1520 {
1521     /**/
1522     if (verbose & 1)
1523         std::cout << name() << " get_Vth OC"
1524                   << " Direction=" << (getDriving()?"OUT":"IN")
1525                   << " DrivingState=" << getDrivingState()
1526                   << " bDrivenState=" << bDrivenState
1527                   << " Vth=" << Vth
1528                   << " VthIn=" << VthIn
1529                   << " Vpullup=" << Vpullup
1530                   << " bPullUp=" << bPullUp << '\n';
1531     /**/
1532 
1533     if (getDriving())
1534         return getDrivingState() ? Vth : 0.0;
1535 
1536     return bPullUp ? Vpullup : VthIn;
1537 }
1538 
get_Zth()1539 double IO_open_collector::get_Zth()
1540 {
1541     float Z;
1542     if (OpenDrain)
1543     {
1544         if (getDriving() && !getDrivingState())
1545             Z = Zth;
1546         else
1547             Z = bPullUp ? Zpullup : ZthIn;
1548     }
1549     else
1550     {
1551         if (getDriving())
1552         {
1553             Z = Zth;
1554         }
1555         else
1556             Z =  bPullUp ? Zpullup : ZthIn;
1557     }
1558     if (verbose)
1559     {
1560         std::cout << name() << " get_Zth OC"
1561                   << " Direction=" << (getDriving()?"OUT":"IN")
1562                   << " DrivingState=" << getDrivingState()
1563                   << " bDrivenState=" << bDrivenState
1564                   << " OpenDrain=" << OpenDrain
1565                   << " Zth=" << Zth
1566                   << " ZthIn=" << ZthIn
1567                   << " Z=" << Z
1568                   << " Zpullup=" << Zpullup
1569                   << " bPullUp=" << bPullUp << '\n';
1570     }
1571     return Z;
1572 }
1573 
getBitChar()1574 char IO_open_collector::getBitChar()
1575 {
1576     if (!snode && !getDriving())
1577     {
1578         char cForced = getForcedDrivenState();
1579         return (cForced == 'Z' && bPullUp) ? 'W' : cForced;
1580     }
1581 
1582     if (snode)
1583     {
1584 
1585         if (snode->get_nodeZth() > ZthFloating)
1586             return bPullUp ? 'W' : 'Z';
1587 
1588         if (getDriving() && getDrivenState() && !getDrivingState())
1589             return 'X';
1590 
1591         if (snode->get_nodeZth() > ZthWeak)
1592             return getDrivenState() ? 'W' : 'w';
1593         else
1594             return getDrivenState() ? '1' : '0';
1595     }
1596 
1597     return getDrivingState() ? 'W' : '0';
1598 }
1599 
1600 
1601 //========================================================================
1602 
1603 //========================================================================
1604 //
1605 // ValueStimulus
1606 //
1607 // A Value stimulus is a stream of data that can change values at
1608 // arbitrary times. An array called 'transition_cycles' stores the times
1609 // and an array 'values' stores the values.
1610 //   When first initialized, the stimulus is driven to its initial state.
1611 // A break point is set on the cycle counter for the next cpu cycle that
1612 // the stimulus is expected to change values. When the break occurs,
1613 // the current state is updated to the next value  and then a break is set
1614 // for the next expect change. This cycle occurs until all of the values
1615 // have been generated. When the end is reached, the asynchronous stimulus
1616 // will restart from the beginning. The member variable 'period' describes
1617 // the magnitude of the rollover (if it's zero then there is no rollover).
1618 //
1619 
ValueStimulus(const char * n)1620 ValueStimulus::ValueStimulus(const char *n)
1621     : source_stimulus()
1622 {
1623     initial.time = 0;
1624     initial.v = 0;
1625     current = 0;
1626     next_sample.time = 0;
1627     next_sample.v = nullptr;
1628 
1629     if (n)
1630     {
1631         new_name(n, false);
1632     }
1633     else
1634     {
1635         char name_str[100];
1636         snprintf(name_str, sizeof(name_str), "s%d_asynchronous_stimulus", num_stimuli);
1637         num_stimuli++;
1638         new_name(name_str, false);
1639     }
1640 }
1641 
get(char * return_str,int len)1642 void ValueStimulus::get(char *return_str, int len)
1643 {
1644     if (return_str)
1645     {
1646         double  d = get_Vth();
1647         snprintf(return_str, len, "%.2fV", d);
1648     }
1649 }
1650 
~ValueStimulus()1651 ValueStimulus::~ValueStimulus()
1652 {
1653     delete initial.v;
1654     delete current;
1655 
1656     for (sample_iterator = samples.begin();
1657             sample_iterator != samples.end();
1658             ++sample_iterator)
1659     {
1660 
1661         delete (*sample_iterator).v;
1662     }
1663 }
1664 
show()1665 void ValueStimulus::show()
1666 {
1667     // print the electrical stuff
1668     stimulus::show();
1669 
1670     std::cout << "\n  states = " << samples.size() << '\n';
1671 
1672     std::list<ValueStimulusData>::iterator si;
1673 
1674     for (si = samples.begin();
1675             si != samples.end();
1676             ++si)
1677     {
1678 
1679         //double d;
1680         //(*si).v->get(d);
1681         std::cout << "    t=" << std::dec << (*si).time
1682                   <<  ",v=" << (*si).v->toString()
1683                   << '\n';
1684 
1685     }
1686 
1687     if (initial.v)
1688         std::cout << "  initial=" << initial.v->toString() << '\n';
1689 
1690     std::cout
1691             << "  period=" << period << '\n'
1692             << "  start_cycle=" << start_cycle << '\n'
1693             << "  Next break cycle=" << future_cycle << '\n';
1694 }
1695 
callback()1696 void ValueStimulus::callback()
1697 {
1698     guint64 current_cycle = future_cycle;
1699 
1700     current = next_sample.v;
1701 
1702     if (verbose & 1)
1703         std::cout << "asynchro cycle " << current_cycle << "  state " << current->toString() << '\n';
1704 
1705     // If there's a node attached to this stimulus, then update it.
1706     if (snode)
1707         snode->update();
1708 
1709     ValueStimulusData *n = getNextSample();
1710 
1711     if (n)
1712     {
1713         next_sample = *n;
1714 
1715         if (verbose & 1)
1716         {
1717             std::cout << "  current_sample (" << next_sample.time << ","
1718                       << next_sample.v->toString() << ")\n";
1719             std::cout << " start cycle " << start_cycle << '\n';
1720         }
1721 
1722         // get the cycle when the data will change next
1723 
1724         future_cycle = next_sample.time + start_cycle;
1725 
1726         if (future_cycle <= current_cycle)
1727         {
1728 
1729             // There's an error in the data. Set a break on the next simulation cycle
1730             // and see if it can be resolved.
1731 
1732             future_cycle = current_cycle + 1;
1733         }
1734 
1735         get_cycles().set_break(future_cycle, this);
1736     }
1737     else
1738         future_cycle = 0;
1739 
1740     if (verbose & 1)
1741         std::cout <<"  next transition = " << future_cycle << '\n';
1742 }
1743 
put_initial_state(Value * pValue)1744 void ValueStimulus::put_initial_state(Value *pValue)
1745 {
1746     if (pValue && !initial.v)
1747     {
1748         initial.time = 0;
1749         initial.v = pValue->copy();
1750     }
1751 }
1752 
put_data(ValueStimulusData & data_point)1753 void ValueStimulus::put_data(ValueStimulusData &data_point)
1754 {
1755     ValueStimulusData *sample = new ValueStimulusData;
1756     sample->time = data_point.time;
1757     sample->v = data_point.v;
1758     samples.push_back(*sample);
1759 }
1760 
get_Vth()1761 double ValueStimulus::get_Vth()
1762 {
1763     double v = initial_state;
1764     if (current)
1765     {
1766         try
1767         {
1768             current->get(v);
1769             if (digital && v > 0.0)
1770                 v = 5.0;
1771         }
1772 
1773         catch (Error &err)
1774         {
1775             std::cout << "Warning stimulus: " << name() << " failed on: " << err.what() << '\n';
1776         }
1777     }
1778     return v;
1779 }
1780 
start()1781 void ValueStimulus::start()
1782 {
1783     if (verbose & 1)
1784         std::cout << "Starting asynchronous stimulus\n";
1785 
1786     if (period)
1787     {
1788 
1789         // Create a data point for the rollover condition.
1790         // If an initial value was supplied when the stimulus was created, then
1791         // that's what we'll use for the rollover. Otherwise, we'll create
1792         // a rollover based on 'initial_state' (which should be a default value).
1793 
1794         ValueStimulusData vSample;
1795         vSample.time = period;
1796         vSample.v = initial.v ? initial.v : new Float(initial_state);
1797         put_data(vSample);
1798     }
1799 
1800     sample_iterator = samples.begin();
1801 
1802     if (sample_iterator != samples.end())
1803     {
1804         if (digital)
1805             initial_state = (initial_state > 0.0) ? Vth : 0.0;
1806 
1807         current       = initial.v;
1808         next_sample   = *sample_iterator;
1809         future_cycle  = next_sample.time + start_cycle;
1810 
1811         get_cycles().set_break(future_cycle, this);
1812 
1813     }
1814 
1815     if (verbose & 1)
1816         std::cout << "asy should've been started\n";
1817 }
1818 
getNextSample()1819 ValueStimulusData *ValueStimulus::getNextSample()
1820 {
1821     ++sample_iterator;
1822 
1823     if (sample_iterator == samples.end())
1824     {
1825 
1826         // We've gone through all of the data. Now let's try to start over
1827 
1828         sample_iterator = samples.begin();
1829 
1830         // If the period is zero then we don't want to
1831         // regenerate the data stream.
1832 
1833         if (period == 0)
1834             return nullptr;
1835 
1836         start_cycle += period;
1837 
1838         if (verbose & 1)
1839         {
1840             std::cout << "  asynchronous stimulus rolled over\n"
1841                       << "   next start_cycle " << start_cycle << "  period " << period << '\n';
1842         }
1843     }
1844 
1845     return &(*sample_iterator);
1846 }
1847 
1848 //------------------------------------------------------------------------
AttributeStimulus(const char * n)1849 AttributeStimulus::AttributeStimulus(const char *n)
1850     : ValueStimulus(n), attr(0)
1851 {
1852 }
1853 
1854 /*
1855 AttributeStimulus::~AttributeStimulus()
1856 {
1857   ValueStimulus::~ValueStimulus();
1858 
1859 }
1860 */
1861 
show()1862 void AttributeStimulus::show()
1863 {
1864     if (attr)
1865         std::cout << "\nDriving Attribute:" << attr->name() << '\n';
1866     ValueStimulus::show();
1867 }
1868 
callback()1869 void AttributeStimulus::callback()
1870 {
1871     guint64 current_cycle = future_cycle;
1872 
1873     current = next_sample.v;
1874 
1875     if (verbose & 1)
1876         std::cout << "asynchro cycle " << current_cycle << "  state " << current->toString() << '\n';
1877 
1878     // If there's a node attached to this stimulus, then update it.
1879     if (attr)
1880         attr->set(current);
1881 
1882     ValueStimulusData *n = getNextSample();
1883 
1884     if (n)
1885     {
1886         next_sample = *n;
1887 
1888         if (verbose & 1)
1889         {
1890             std::cout << "  current_sample (" << next_sample.time << ","
1891                       << next_sample.v->toString() << ")\n";
1892             std::cout << " start cycle " << start_cycle << '\n';
1893         }
1894 
1895         // get the cycle when the data will change next
1896 
1897         future_cycle = next_sample.time + start_cycle;
1898 
1899 
1900         if (future_cycle <= current_cycle)
1901         {
1902 
1903             // There's an error in the data. Set a break on the next simulation cycle
1904             // and see if it can be resolved.
1905 
1906             future_cycle = current_cycle + 1;
1907         }
1908 
1909         get_cycles().set_break(future_cycle, this);
1910     }
1911     else
1912         future_cycle = 0;
1913 
1914     if (verbose & 1)
1915         std::cout <<"  next transition = " << future_cycle << '\n';
1916 }
1917 
setClientAttribute(Value * v)1918 void AttributeStimulus::setClientAttribute(Value *v)
1919 {
1920     if (attr)
1921         std::cout << "overwriting target attribute in AttributeStimulus\n";
1922 
1923     attr = v;
1924 
1925     if ((bool)verbose && v)
1926         std::cout << " attached " << name() << " to attribute: " << v->name() << '\n';
1927 }
1928 
1929 //========================================================================
1930 //
1931 // helper functions follow here
1932 
1933 
stimuli_attach(gpsimObject * pNode,gpsimObjectList_t * pPinList)1934 void stimuli_attach(gpsimObject *pNode, gpsimObjectList_t *pPinList)
1935 {
1936     if (!pNode || !pPinList)
1937         return;
1938 
1939     if (verbose)
1940         std::cout << __FUNCTION__ << " pNode " << pNode->name() << '\n';
1941 
1942     gpsimObjectList_t :: iterator si = pPinList->begin();
1943 
1944     Stimulus_Node *psn = dynamic_cast<Stimulus_Node *>(pNode);
1945     if (psn)
1946     {
1947 
1948         //cout << __FUNCTION__ << " pNode " << pNode->name() << " is a Stimulus_Node\n";
1949 
1950         while (si != pPinList->end())
1951         {
1952             stimulus *ps = dynamic_cast<stimulus *> (*si);
1953             if (ps)
1954                 psn->attach_stimulus(ps);
1955             ++si;
1956         }
1957 
1958         psn->update();
1959 
1960         return;
1961     }
1962 
1963     AttributeStimulus *ast = dynamic_cast<AttributeStimulus *>(pNode);
1964     if (ast)
1965     {
1966         Value *v = dynamic_cast<Value *>(*si);
1967         if (v)
1968             ast->setClientAttribute(v);
1969 
1970         if (verbose)
1971         {
1972             std::cout << __FUNCTION__ << " pNode " << pNode->name() << " is an attribute stimulus\n";
1973             if (v)
1974                 std::cout << __FUNCTION__ << " connecting " << v->name() << '\n';
1975         }
1976     }
1977 }
1978