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