KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kibis.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2022 Fabien Corona f.corona<at>laposte.net
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * Redistribution and use in source and binary forms, with or without modification,
8 * are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
18 * to endorse or promote products derived from this software without specific
19 * prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33#include "kibis.h"
34#include "ibis_parser.h"
35#include <sstream>
36#include <sim/spice_simulator.h>
37
38
39// _() is used here to mark translatable strings in IBIS_REPORTER::Report()
40// However, currently non ASCII7 chars are nor correctly handled when printing messages
41// So we disable translations
42#if 0
43#include <wx/intl.h> // for _() macro and wxGetTranslation()
44#else
45#undef _
46#define _( x ) x
47#endif
48
49
50std::vector<std::pair<int, double>>
51SimplifyBitSequence( const std::vector<std::pair<int, double>>& bits )
52{
53 std::vector<std::pair<int, double>> result;
54 int prevbit = -1;
55
56 for( const std::pair<int, double>& bit : bits )
57 {
58 if( prevbit != bit.first )
59 result.push_back( bit );
60
61 prevbit = bit.first;
62 }
63
64 return result;
65}
66
67
69 KIBIS_BASE( aTopLevel, aTopLevel->m_Reporter )
70{
71}
72
73
74KIBIS_BASE::KIBIS_BASE( KIBIS* aTopLevel, REPORTER* aReporter ) :
75 IBIS_BASE( aReporter ),
76 m_topLevel( aTopLevel ),
77 m_valid( false )
78{
79}
80
81
83{
85
86 if( aIn == IBIS_CORNER::MIN )
87 out = IBIS_CORNER::MAX;
88 else if( aIn == IBIS_CORNER::MAX )
89 out = IBIS_CORNER::MIN;
90
91 return out;
92}
93
94KIBIS::KIBIS( const std::string& aFileName, REPORTER* aReporter ) :
95 KIBIS_BASE( this, aReporter ),
96 m_file( *this )
97{
98 IbisParser parser( m_Reporter );
99 bool status = true;
100
101 parser.m_parrot = false;
102 status &= parser.ParseFile( aFileName );
103
104 status &= m_file.Init( parser );
105
106 for( const IbisModel& iModel : parser.m_ibisFile.m_models )
107 {
108 KIBIS_MODEL& kModel = m_models.emplace_back( *this, iModel, parser );
109 status &= kModel.m_valid;
110 }
111
112 for( const IbisComponent& iComponent : parser.m_ibisFile.m_components )
113 {
114 KIBIS_COMPONENT& kComponent = m_components.emplace_back( *this, iComponent, parser );
115 status &= kComponent.m_valid;
116
117 for( KIBIS_PIN& pin : kComponent.m_pins )
118 pin.m_parent = &kComponent;
119
120 for( const IbisDiffPinEntry& dpEntry : iComponent.m_diffPin.m_entries )
121 {
122 KIBIS_PIN* pinA = kComponent.GetPin( dpEntry.pinA );
123 KIBIS_PIN* pinB = kComponent.GetPin( dpEntry.pinB );
124
125 if( pinA && pinB )
126 {
127 pinA->m_complementaryPin = pinB;
128 pinB->m_complementaryPin = pinA;
129 }
130 }
131 }
132
133 m_valid = status;
134}
135
136
138 KIBIS_BASE( &aTopLevel )
139{
140 m_fileRev = -1;
141 m_ibisVersion = -1;
142}
143
144
145bool KIBIS_FILE::Init( const IbisParser& aParser )
146{
147 bool status = true;
155
156 m_valid = status;
157
158 return status;
159}
160
161
163 const IbisComponentPackage& aPackage, IbisParser& aParser,
164 KIBIS_COMPONENT* aParent, std::vector<KIBIS_MODEL>& aModels ) :
165 KIBIS_BASE( &aTopLevel ),
166 m_Rpin( aTopLevel.m_Reporter ),
167 m_Lpin( aTopLevel.m_Reporter ),
168 m_Cpin( aTopLevel.m_Reporter )
169{
171 m_pinNumber = aPin.m_pinName;
172 m_parent = aParent;
173
174 m_Rpin = aPackage.m_Rpkg;
175 m_Lpin = aPackage.m_Lpkg;
176 m_Cpin = aPackage.m_Cpkg;
177
178 // The values listed in the [Pin] description section override the default
179 // values defined in [Package]
180
181 // @TODO : Reading the IBIS standard, I can't figure out if we are supposed
182 // to replace typ, min, and max, or just the typ ?
183
184 if( !std::isnan( aPin.m_Rpin ) )
185 {
186 m_Rpin.value[IBIS_CORNER::TYP] = aPin.m_Rpin;
187 m_Rpin.value[IBIS_CORNER::MIN] = aPin.m_Rpin;
188 m_Rpin.value[IBIS_CORNER::MAX] = aPin.m_Rpin;
189 }
190
191 if( !std::isnan( aPin.m_Lpin ) )
192 {
193 m_Lpin.value[IBIS_CORNER::TYP] = aPin.m_Lpin;
194 m_Lpin.value[IBIS_CORNER::MIN] = aPin.m_Lpin;
195 m_Lpin.value[IBIS_CORNER::MAX] = aPin.m_Lpin;
196 }
197
198 if( !std::isnan( aPin.m_Cpin ) )
199 {
200 m_Cpin.value[IBIS_CORNER::TYP] = aPin.m_Cpin;
201 m_Cpin.value[IBIS_CORNER::MIN] = aPin.m_Cpin;
202 m_Cpin.value[IBIS_CORNER::MAX] = aPin.m_Cpin;
203 }
204
205 bool modelSelected = false;
206 std::vector<std::string> listOfModels;
207
208 for( const IbisModelSelector& modelSelector : aParser.m_ibisFile.m_modelSelectors )
209 {
210 if( !strcmp( modelSelector.m_name.c_str(), aPin.m_modelName.c_str() ) )
211 {
212 for( const IbisModelSelectorEntry& model : modelSelector.m_models )
213 listOfModels.push_back( model.m_modelName );
214
215 modelSelected = true;
216 break;
217 }
218 }
219
220 if( !modelSelected )
221 listOfModels.push_back( aPin.m_modelName );
222
223 for( const std::string& modelName : listOfModels )
224 {
225 for( KIBIS_MODEL& model : aModels )
226 {
227 if( !strcmp( model.m_name.c_str(), modelName.c_str() ) )
228 m_models.push_back( &model );
229 }
230 }
231
232 m_valid = true;
233}
234
235
237 KIBIS_BASE( &aTopLevel ),
238 m_GNDClamp( aTopLevel.m_Reporter ),
239 m_POWERClamp( aTopLevel.m_Reporter )
240{
241 m_name = aSource.m_name;
242 m_type = aSource.m_type;
243 m_mode = aMode;
244
245 m_GNDClamp = aSource.m_GNDClamp;
246 m_POWERClamp = aSource.m_POWERClamp;
247
248 m_valid = true;
249}
250
252{
253 return m_GNDClamp.m_entries.size() > 0;
254}
255
256
258{
259 return m_POWERClamp.m_entries.size() > 0;
260}
261
262
263KIBIS_MODEL::KIBIS_MODEL( KIBIS& aTopLevel, const IbisModel& aSource, IbisParser& aParser ) :
264 KIBIS_BASE( &aTopLevel ),
265 m_C_comp( aTopLevel.m_Reporter ),
266 m_voltageRange( aTopLevel.m_Reporter ),
267 m_temperatureRange( aTopLevel.m_Reporter ),
268 m_pullupReference( aTopLevel.m_Reporter ),
269 m_pulldownReference( aTopLevel.m_Reporter ),
270 m_GNDClampReference( aTopLevel.m_Reporter ),
272 m_Rgnd( aTopLevel.m_Reporter ),
273 m_Rpower( aTopLevel.m_Reporter ),
274 m_Rac( aTopLevel.m_Reporter ),
275 m_Cac( aTopLevel.m_Reporter ),
276 m_GNDClamp( aTopLevel.m_Reporter ),
277 m_POWERClamp( aTopLevel.m_Reporter ),
278 m_pullup( aTopLevel.m_Reporter ),
279 m_pulldown( aTopLevel.m_Reporter ),
280 m_ramp( aTopLevel.m_Reporter )
281{
282 bool status = true;
283
284 m_name = aSource.m_name;
285 m_type = aSource.m_type;
286
287 m_description = std::string( "No description available." );
288
289 for( const IbisModelSelector& modelSelector : aParser.m_ibisFile.m_modelSelectors )
290 {
291 for( const IbisModelSelectorEntry& entry : modelSelector.m_models )
292 {
293 if( !strcmp( entry.m_modelName.c_str(), m_name.c_str() ) )
295 }
296 }
297
298 m_vinh = aSource.m_vinh;
299 m_vinl = aSource.m_vinl;
300 m_vref = aSource.m_vref;
301 m_rref = aSource.m_rref;
302 m_cref = aSource.m_cref;
303 m_vmeas = aSource.m_vmeas;
304
305 m_enable = aSource.m_enable;
306 m_polarity = aSource.m_polarity;
307
308 m_ramp = aSource.m_ramp;
311 m_GNDClamp = aSource.m_GNDClamp;
313 m_POWERClamp = aSource.m_POWERClamp;
315
316 if( aSource.m_C_comp.isNA() )
317 {
318 m_C_comp.value[IBIS_CORNER::TYP] = 0.0;
319 m_C_comp.value[IBIS_CORNER::MIN] = 0.0;
320 m_C_comp.value[IBIS_CORNER::MAX] = 0.0;
321 m_C_comp.Add( aSource.m_C_comp_pullup );
322 m_C_comp.Add( aSource.m_C_comp_pulldown );
323 m_C_comp.Add( aSource.m_C_comp_gnd_clamp );
324 m_C_comp.Add( aSource.m_C_comp_power_clamp );
325 }
326 else
327 {
328 m_C_comp = aSource.m_C_comp;
329 }
334
335 m_Rgnd = aSource.m_Rgnd;
336 m_Rpower = aSource.m_Rpower;
337 m_Rac = aSource.m_Rac;
338 m_Cac = aSource.m_Cac;
339 m_pullup = aSource.m_pullup;
340 m_pulldown = aSource.m_pulldown;
341
342 for( const IbisSubmodelMode& submodel : aSource.m_submodels )
343 {
344 auto it = aParser.m_ibisFile.m_submodels.find( submodel.m_name );
345
346 if( it != aParser.m_ibisFile.m_submodels.end() )
347 {
348 const IbisSubmodel& submodelSource = it->second;
349
350 // For now, we only support static mode dynamic clamp submodels.
351 if( submodelSource.m_type != IBIS_SUBMODEL_TYPE::DYNAMIC_CLAMP
352 || !submodelSource.m_VtriggerR.isNA()
353 || !submodelSource.m_VtriggerF.isNA() )
354 {
355 break;
356 }
357
358 m_submodels.emplace_back( aTopLevel, submodelSource, submodel.m_mode );
359 }
360 }
361
362 m_valid = status;
363}
364
365
367 IbisParser& aParser ) :
368 KIBIS_BASE( &aTopLevel )
369{
370 bool status = true;
371
372 m_name = aSource.m_name;
374
375 for( const IbisComponentPin& iPin : aSource.m_pins )
376 {
377 if( iPin.m_dummy )
378 continue;
379
380 KIBIS_PIN kPin( aTopLevel, iPin, aSource.m_package, aParser, nullptr,
381 m_topLevel->m_models );
382 status &= kPin.m_valid;
383 m_pins.push_back( kPin );
384 }
385
386 m_valid = status;
387}
388
389
390KIBIS_PIN* KIBIS_COMPONENT::GetPin( const std::string& aPinNumber )
391{
392 for( KIBIS_PIN& pin : m_pins )
393 {
394 if( pin.m_pinNumber == aPinNumber )
395 return &pin;
396 }
397
398 return nullptr;
399}
400
401std::vector<std::pair<IbisWaveform*, IbisWaveform*>> KIBIS_MODEL::waveformPairs()
402{
403 std::vector<std::pair<IbisWaveform*, IbisWaveform*>> pairs;
404 IbisWaveform* wf1;
405 IbisWaveform* wf2;
406
407 for( size_t i = 0; i < m_risingWaveforms.size(); i++ )
408 {
409 for( size_t j = 0; j < m_fallingWaveforms.size(); j++ )
410 {
411 wf1 = m_risingWaveforms.at( i );
412 wf2 = m_fallingWaveforms.at( j );
413
414 if( wf1->m_R_fixture == wf2->m_R_fixture && wf1->m_L_fixture == wf2->m_L_fixture
415 && wf1->m_C_fixture == wf2->m_C_fixture && wf1->m_V_fixture == wf2->m_V_fixture
416 && wf1->m_V_fixture_min == wf2->m_V_fixture_min
417 && wf1->m_V_fixture_max == wf2->m_V_fixture_max )
418 {
419 pairs.emplace_back( wf1, wf2 );
420 }
421 }
422 }
423
424 return pairs;
425}
426
427
428std::string KIBIS_MODEL::SpiceDie( const KIBIS_PARAMETER& aParam, int aIndex, bool aDriver ) const
429{
430 std::string result;
431
432 std::string GC_GND = "GC_GND";
433 std::string PC_PWR = "PC_PWR";
434 std::string PU_PWR = "PU_PWR";
435 std::string PD_GND = "PD_GND";
436 std::string DIE = "DIE";
437 std::string DIEBUFF = "DIEBUFF";
438
439 IBIS_CORNER supply = aParam.m_supply;
440 IBIS_CORNER ccomp = aParam.m_Ccomp;
441
442 GC_GND += std::to_string( aIndex );
443 PC_PWR += std::to_string( aIndex );
444 PU_PWR += std::to_string( aIndex );
445 PD_GND += std::to_string( aIndex );
446 DIE += std::to_string( aIndex );
447 DIEBUFF += std::to_string( aIndex );
448
449
450 std::string GC = "GC";
451 std::string PC = "PC";
452 std::string PU = "PU";
453 std::string PD = "PD";
454
455 GC += std::to_string( aIndex );
456 PC += std::to_string( aIndex );
457 PU += std::to_string( aIndex );
458 PD += std::to_string( aIndex );
459
460 result = "\n";
461 result += "VPWR POWER GND ";
462 result += doubleToString( m_voltageRange.value[supply] );
463 result += "\n";
464 result += "CCPOMP " + DIE + " GND ";
465 result += doubleToString( m_C_comp.value[ccomp] );
466 result += "\n";
467
468 if( HasGNDClamp() )
469 {
470 result += m_GNDClamp.Spice( aIndex * 4 + 1, DIE, GC_GND, false, GC, supply );
471 result += "Vmeas" + GC + " GND " + GC_GND + " 0\n";
472 }
473
474 if( HasPOWERClamp() )
475 {
476 result += m_POWERClamp.Spice( aIndex * 4 + 2, PC_PWR, DIE, true, PC, supply );
477 result += "Vmeas" + PC + " POWER " + PC_PWR + " 0\n";
478 }
479
480 if( aDriver && HasPulldown() )
481 {
482 result += m_pulldown.Spice( aIndex * 4 + 3, DIEBUFF, PD_GND, false, PD, supply );
483 result += "VmeasPD GND " + PD_GND + " 0\n";
484 result += "BKD GND " + DIE + " i=( i(VmeasPD) * v(KD) )\n";
485 }
486
487 if( aDriver && HasPullup() )
488 {
489 result += m_pullup.Spice( aIndex * 4 + 4, PU_PWR, DIEBUFF, true, PU, supply );
490 result += "VmeasPU POWER " + PU_PWR + " 0\n";
491 result += "BKU POWER " + DIE + " i=( -i(VmeasPU) * v(KU) )\n";
492 }
493
494 if( aDriver && ( HasPullup() || HasPulldown() ) )
495 result += "BDIEBUFF " + DIEBUFF + " GND v=v(" + DIE + ")\n";
496
497 for( const KIBIS_SUBMODEL& submodel : m_submodels )
498 {
499 if( aDriver && ( submodel.m_mode == IBIS_SUBMODEL_MODE::NON_DRIVING ) )
500 continue;
501
502 if( !aDriver && ( submodel.m_mode == IBIS_SUBMODEL_MODE::DRIVING ) )
503 continue;
504
505 aIndex++;
506
507 GC_GND = "GC_GND" + std::to_string( aIndex );
508 PC_PWR = "PC_PWR" + std::to_string( aIndex );
509
510 GC = "GC" + std::to_string( aIndex );
511 PC = "PC" + std::to_string( aIndex );
512
513 if( submodel.HasGNDClamp() )
514 {
515 result += submodel.m_GNDClamp.Spice( aIndex * 4 + 1, DIE, GC_GND, false, GC, supply );
516 result += "Vmeas" + GC + " GND " + GC_GND + " 0\n";
517 }
518
519 if( submodel.HasPOWERClamp() )
520 {
521 result += submodel.m_POWERClamp.Spice( aIndex * 4 + 2, PC_PWR, DIE, true, PC, supply );
522 result += "Vmeas" + PC + " POWER " + PC_PWR + " 0\n";
523 }
524 }
525
526 return result;
527}
528
529
531{
532 IbisWaveform out( aIn.m_Reporter );
533
534 const int nbPoints = aIn.m_table.m_entries.size();
535
536 if( nbPoints < 2 )
537 {
538 Report( _( "waveform has less than two points" ), RPT_SEVERITY_ERROR );
539 return out;
540 }
541
542 const double DCtyp = aIn.m_table.m_entries[0].V.value[IBIS_CORNER::TYP];
543 const double DCmin = aIn.m_table.m_entries[0].V.value[IBIS_CORNER::MIN];
544 const double DCmax = aIn.m_table.m_entries[0].V.value[IBIS_CORNER::MAX];
545
546 if( nbPoints == 2 )
547 return out;
548
549 out.m_table.m_entries.clear();
550
551 for( int i = 0; i < nbPoints; i++ )
552 {
553 const VTtableEntry& inEntry = aIn.m_table.m_entries.at( i );
554 VTtableEntry& outEntry = out.m_table.m_entries.emplace_back( out.m_Reporter );
555
556 outEntry.t = inEntry.t;
557 outEntry.V.value[IBIS_CORNER::TYP] = inEntry.V.value[IBIS_CORNER::TYP] - DCtyp;
558 outEntry.V.value[IBIS_CORNER::MIN] = inEntry.V.value[IBIS_CORNER::MIN] - DCmin;
559 outEntry.V.value[IBIS_CORNER::MAX] = inEntry.V.value[IBIS_CORNER::MAX] - DCmax;
560 }
561
562 return out;
563}
564
565
567{
568 return m_pulldown.m_entries.size() > 0;
569}
570
571
573{
574 return m_pullup.m_entries.size() > 0;
575}
576
577
579{
580 return m_GNDClamp.m_entries.size() > 0;
581}
582
583
585{
586 return m_POWERClamp.m_entries.size() > 0;
587}
588
589
590std::string KIBIS_MODEL::generateSquareWave( const std::string& aNode1, const std::string& aNode2,
591 const std::vector<std::pair<int, double>>& aBits,
592 const std::pair<IbisWaveform*, IbisWaveform*>& aPair,
593 const KIBIS_PARAMETER& aParam )
594{
595 const IBIS_CORNER supply = aParam.m_supply;
596 std::string simul;
597 std::vector<int> stimuliIndex;
598
599 IbisWaveform risingWF = TrimWaveform( *( aPair.first ) );
600 IbisWaveform fallingWF = TrimWaveform( *( aPair.second ) );
601
602 double deltaR = risingWF.m_table.m_entries.back().V.value[supply]
603 - risingWF.m_table.m_entries.at( 0 ).V.value[supply];
604 double deltaF = fallingWF.m_table.m_entries.back().V.value[supply]
605 - fallingWF.m_table.m_entries.at( 0 ).V.value[supply];
606
607 // Ideally, delta should be equal to zero.
608 // It can be different from zero if the falling waveform does not start were the rising one ended.
609 double delta = deltaR + deltaF;
610
611 int i = 0;
612
613 int prevBit = 2;
614
615 for( const std::pair<int, double>& bit : aBits )
616 {
618 double timing = bit.second;
619
620
621 if ( bit.first != prevBit )
622 {
623 if( bit.first == 1 )
624 WF = &risingWF;
625 else
626 WF = &fallingWF;
627
628 stimuliIndex.push_back( i );
629
630 simul += "Vstimuli";
631 simul += std::to_string( i );
632 simul += " stimuli";
633 simul += std::to_string( i );
634 simul += " ";
635 simul += aNode2;
636 simul += " pwl ( \n+";
637
638 if( i != 0 )
639 {
640 simul += "0 0 ";
641 VTtableEntry entry0 = WF->m_table.m_entries.at( 0 );
642 VTtableEntry entry1 = WF->m_table.m_entries.at( 1 );
643 double deltaT = entry1.t - entry0.t;
644
645 simul += doubleToString( entry0.t + timing - deltaT );
646 simul += " ";
647 simul += "0";
648 simul += "\n+";
649 }
650
651 for( const VTtableEntry& entry : WF->m_table.m_entries )
652 {
653 simul += doubleToString( entry.t + timing );
654 simul += " ";
655 simul += doubleToString( entry.V.value[supply] - delta );
656 simul += "\n+";
657 }
658
659 simul += ")\n";
660 }
661
662 i++;
663 prevBit = bit.first;
664 }
665
666 simul += "bin ";
667 simul += aNode1;
668 simul += " ";
669 simul += aNode2;
670 simul += " v=(";
671
672 for( int ii: stimuliIndex )
673 {
674 simul += " v( stimuli";
675 simul += std::to_string( ii );
676 simul += " ) +\n+";
677 }
678
679 // Depending on the first bit, we add a different DC value
680 // The DC value we add is the first value of the first bit.
681 if( ( aBits.size() > 0 ) && ( aBits[0].first == 0 ) )
682 simul += doubleToString( aPair.second->m_table.m_entries.at( 0 ).V.value[supply] );
683 else
684 simul += doubleToString( aPair.first->m_table.m_entries.at( 0 ).V.value[supply] );
685
686 simul += ")\n";
687 return simul;
688}
689
690
691std::string KIBIS_PIN::addDie( KIBIS_MODEL& aModel, const KIBIS_PARAMETER& aParam, int aIndex )
692{
693 const IBIS_CORNER supply = aParam.m_supply;
694 std::string simul;
695
696 std::string GC_GND = "GC_GND";
697 std::string PC_PWR = "PC_PWR";
698 std::string PU_PWR = "PU_PWR";
699 std::string PD_GND = "PD_GND";
700 std::string DIE = "DIE";
701
702 GC_GND += std::to_string( aIndex );
703 PC_PWR += std::to_string( aIndex );
704 PU_PWR += std::to_string( aIndex );
705 PD_GND += std::to_string( aIndex );
706 DIE += std::to_string( aIndex );
707
708
709 std::string GC = "GC";
710 std::string PC = "PC";
711 std::string PU = "PU";
712 std::string PD = "PD";
713
714 GC += std::to_string( aIndex );
715 PC += std::to_string( aIndex );
716 PU += std::to_string( aIndex );
717 PD += std::to_string( aIndex );
718
719 if( aModel.HasGNDClamp() )
720 simul += aModel.m_GNDClamp.Spice( aIndex * 4 + 1, DIE, GC_GND, false, GC, supply );
721
722 if( aModel.HasPOWERClamp() )
723 simul += aModel.m_POWERClamp.Spice( aIndex * 4 + 2, PC_PWR, DIE, true, PC, supply );
724
725 if( aModel.HasPulldown() )
726 simul += aModel.m_pulldown.Spice( aIndex * 4 + 3, DIE, PD_GND, false, PD, supply );
727
728 if( aModel.HasPullup() )
729 simul += aModel.m_pullup.Spice( aIndex * 4 + 4, PU_PWR, DIE, true, PU, supply );
730
731 return simul;
732}
733
734
735void KIBIS_PIN::getKuKdFromFile( const std::string& aSimul )
736{
737 const std::string outputFileName = m_topLevel->m_cacheDir + "temp_output.spice";
738
739 if( std::remove( outputFileName.c_str() ) )
740 Report( _( "Cannot remove temporary output file" ), RPT_SEVERITY_WARNING );
741
742 std::shared_ptr<SPICE_SIMULATOR> ng = SIMULATOR::CreateInstance( "ng-kibis" );
743
744 if( !ng )
745 throw std::runtime_error( "Could not create simulator instance" );
746
747 ng->Init();
748 ng->LoadNetlist( aSimul );
749
750 std::ifstream KuKdfile;
751 KuKdfile.open( outputFileName );
752
753 std::vector<double> ku, kd, t;
754
755 if( KuKdfile )
756 {
757 std::string line;
758 bool valuesTagFound = false;
759
760 while( std::getline( KuKdfile, line ) ) // skip ngspice output header
761 {
762 if( line.find( "Values:" ) != std::string::npos )
763 {
764 valuesTagFound = true;
765 break;
766 }
767 }
768
769 if( !valuesTagFound )
770 {
771 Report( _( "Missing 'Values:' tag in temporary file" ), RPT_SEVERITY_ERROR );
772 }
773
774 int i = 0;
775 double t_v, ku_v, kd_v;
776
777 try
778 {
779 while( KuKdfile )
780 {
781 std::getline( KuKdfile, line );
782
783 if( line.empty() )
784 continue;
785
786 switch( i )
787 {
788 case 0:
789 line = line.substr( line.find_first_of( "\t" ) + 1 );
790 t_v = std::stod( line );
791 break;
792 case 1: ku_v = std::stod( line ); break;
793 case 2:
794 kd_v = std::stod( line );
795 ku.push_back( ku_v );
796 kd.push_back( kd_v );
797 t.push_back( t_v );
798 break;
799 default: Report( _( "Error while reading temporary file" ), RPT_SEVERITY_ERROR );
800 }
801
802 i = ( i + 1 ) % 3;
803 }
804 }
805 catch( const std::exception& )
806 {
807 Report( _( "Error while reading temporary file" ), RPT_SEVERITY_ERROR );
808 }
809
810 std::getline( KuKdfile, line );
811 }
812 else
813 {
814 Report( _( "Error while creating temporary output file" ), RPT_SEVERITY_ERROR );
815 }
816
817 if( std::remove( outputFileName.c_str() ) )
818 {
819 Report( _( "Cannot remove temporary output file" ), RPT_SEVERITY_WARNING );
820 }
821
822 m_Ku = std::move( ku );
823 m_Kd = std::move( kd );
824 m_t = std::move( t );
825}
826
827
829 const std::pair<IbisWaveform*, IbisWaveform*>& aPair,
830 const KIBIS_PARAMETER& aParam, int aIndex )
831{
832 IBIS_CORNER supply = aParam.m_supply;
833 IBIS_CORNER ccomp = aParam.m_Ccomp;
834 KIBIS_WAVEFORM* wave = aParam.m_waveform;
835
836 std::string simul = "";
837
838 simul += "*THIS IS NOT A VALID SPICE MODEL.\n";
839 simul += "*This part is intended to be executed by Kibis internally.\n";
840 simul += "*You should not be able to read this.\n\n";
841
842 simul += ".SUBCKT DRIVER";
843 simul += std::to_string( aIndex );
844 simul += " POWER GND PIN \n"; // 1: POWER, 2:GND, 3:PIN
845
846 simul += "Vdummy 2 PIN 0\n";
847
848 if( ( aPair.first->m_R_dut != 0 ) || ( aPair.first->m_L_dut != 0 )
849 || ( aPair.first->m_C_dut != 0 ) )
850 {
851 Report( _( "Kibis does not support DUT values yet. "
852 "https://ibis.org/summits/nov16a/chen.pdf" ),
854 }
855
856 simul += "\n";
857 simul += "CCPOMP 2 GND ";
858 simul += doubleToString( aModel.m_C_comp.value[ccomp] ); //@TODO: Check the corner ?
859 simul += "\n";
860
861 const IbisWaveform* risingWF = aPair.first;
862 const IbisWaveform* fallingWF = aPair.second;
863
864 switch( wave->GetType() )
865 {
868 {
869 wave->Check( risingWF, fallingWF );
870 std::vector<std::pair<int, double>> bits = wave->GenerateBitSequence();
871 bits = SimplifyBitSequence( bits );
872 simul += aModel.generateSquareWave( "DIE0", "GND", bits, aPair, aParam );
873 break;
874 }
876 {
877 const IbisWaveform* waveform = wave->inverted ? risingWF : fallingWF;
878 simul += "Vsig DIE0 GND ";
879 simul += doubleToString( waveform->m_table.m_entries.at( 0 ).V.value[supply] );
880 simul += "\n";
881 break;
882 }
884 {
885 const IbisWaveform* waveform = wave->inverted ? fallingWF : risingWF;
886 simul += "Vsig DIE0 GND ";
887 simul += doubleToString( waveform->m_table.m_entries.at( 0 ).V.value[supply] );
888 simul += "\n";
889 break;
890 }
893 default:
894 break;
895 }
896
897 simul += addDie( aModel, aParam, 0 );
898
899 simul += "\n.ENDS DRIVER\n\n";
900 return simul;
901}
902
903
905 const std::pair<IbisWaveform*, IbisWaveform*>& aPair,
906 const KIBIS_PARAMETER& aParam )
907{
908 IBIS_CORNER supply = aParam.m_supply;
909 const KIBIS_WAVEFORM* wave = aParam.m_waveform;
910
911 std::string simul = "";
912
913 if( !wave )
914 return;
915
916 if( wave->GetType() == KIBIS_WAVEFORM_TYPE::NONE )
917 {
918 //@TODO , there could be some current flowing through pullup / pulldown transistors, even when off
919 std::vector<double> ku, kd, t;
920 ku.push_back( 0 );
921 kd.push_back( 0 );
922 t.push_back( 0 );
923 m_Ku = std::move( ku );
924 m_Kd = std::move( kd );
925 m_t = std::move( t );
926 }
927 else
928 {
929 simul += KuKdDriver( aModel, aPair, aParam, 0 );
930 simul += "\n x1 3 0 1 DRIVER0 \n";
931
932 simul += "VCC 3 0 ";
933 simul += doubleToString( aModel.m_voltageRange.value[supply] );
934 simul += "\n";
935 //simul += "Vpin x1.DIE 0 1 \n"
936 simul += "Lfixture 1 4 ";
937 simul += doubleToString( aPair.first->m_L_fixture );
938 simul += "\n";
939 simul += "Rfixture 4 5 ";
940 simul += doubleToString( aPair.first->m_R_fixture );
941 simul += "\n";
942 simul += "Cfixture 4 0 ";
943 simul += doubleToString( aPair.first->m_C_fixture );
944 simul += "\n";
945 simul += "Vfixture 5 0 ";
946 simul += doubleToString( aPair.first->m_V_fixture );
947 simul += "\n";
948 simul += "VmeasIout x1.DIE0 x1.2 0\n";
949 simul += "VmeasPD 0 x1.PD_GND0 0\n";
950 simul += "VmeasPU x1.PU_PWR0 3 0\n";
951 simul += "VmeasPC x1.PC_PWR0 3 0\n";
952 simul += "VmeasGC 0 x1.GC_GND0 0\n";
953
954 if( aModel.HasPullup() && aModel.HasPulldown() )
955 {
956 Report( _( "Model has only one waveform pair, reduced accuracy" ),
958 simul += "Bku KU 0 v=( (i(VmeasIout)-i(VmeasPC)-i(VmeasGC)-i(VmeasPD) "
959 ")/(i(VmeasPU)-i(VmeasPD)))\n";
960 simul += "Bkd KD 0 v=(1-v(KU))\n";
961 }
962
963 else if( !aModel.HasPullup() && aModel.HasPulldown() )
964 {
965 simul += "Bku KD 0 v=( ( i(VmeasIout)+i(VmeasPC)+i(VmeasGC) )/(i(VmeasPD)))\n";
966 simul += "Bkd KU 0 v=0\n";
967 }
968
969 else if( aModel.HasPullup() && !aModel.HasPulldown() )
970 {
971 simul += "Bku KU 0 v=( ( i(VmeasIout)+i(VmeasPC)+i(VmeasGC) )/(i(VmeasPU)))\n";
972 simul += "Bkd KD 0 v=0\n";
973 }
974 else
975 {
976 Report( _( "Driver needs at least a pullup or a pulldown" ), RPT_SEVERITY_ERROR );
977 }
978
979 switch( wave->GetType() )
980 {
983 {
984 double duration = wave->GetDuration();
985 simul += ".tran 0.1n ";
986 simul += doubleToString( duration );
987 simul += "\n";
988 break;
989 }
993 default:
994 simul += ".tran 0.5 1 \n"; //
995 }
996
997 //simul += ".dc Vpin -5 5 0.1\n";
998 simul += ".control run \n";
999 simul += "set filetype=ascii\n";
1000 simul += "run \n";
1001 //simul += "plot v(x1.DIE0) i(VmeasIout) i(VmeasPD) i(VmeasPU) i(VmeasPC) i(VmeasGC)\n";
1002 //simul += "plot v(KU) v(KD)\n";
1003
1004 std::string outputFileName = m_topLevel->m_cacheDir + "temp_output.spice";
1005 simul += "write '" + outputFileName + "' v(KU) v(KD)\n";
1006 simul += "quit\n";
1007 simul += ".endc \n";
1008 simul += ".end \n";
1009
1010 getKuKdFromFile( simul );
1011 }
1012}
1013
1014
1016{
1017 std::vector<double> ku, kd, t;
1018 const KIBIS_WAVEFORM* wave = aParam.m_waveform;
1019 const IBIS_CORNER& supply = aParam.m_supply;
1020
1021 if( !wave )
1022 return;
1023
1024 switch( wave->GetType() )
1025 {
1028 {
1029 wave->Check( aModel.m_ramp.m_rising, aModel.m_ramp.m_falling );
1030 std::vector<std::pair<int, double>> bits = wave->GenerateBitSequence();
1031 bits = SimplifyBitSequence( bits );
1032
1033 for( const std::pair<int, double>& bit : bits )
1034 {
1035 ku.push_back( bit.first ? 0 : 1 );
1036 kd.push_back( bit.first ? 1 : 0 );
1037 t.push_back( bit.second );
1038 ku.push_back( bit.first ? 1 : 0 );
1039 kd.push_back( bit.first ? 0 : 1 );
1040 t.push_back( bit.second
1041 + ( bit.first ? +aModel.m_ramp.m_rising.value[supply].m_dt
1042 : aModel.m_ramp.m_falling.value[supply].m_dt )
1043 / 0.6 );
1044 // 0.6 because ibis only gives 20%-80% time
1045 }
1046 break;
1047 }
1049 {
1050 ku.push_back( wave->inverted ? 0 : 1 );
1051 kd.push_back( wave->inverted ? 1 : 0 );
1052 t.push_back( 0 );
1053 break;
1054 }
1056 {
1057 ku.push_back( wave->inverted ? 1 : 0 );
1058 kd.push_back( wave->inverted ? 0 : 1 );
1059 t.push_back( 0 );
1060 break;
1061 }
1064 default:
1065 ku.push_back( 0 );
1066 kd.push_back( 0 );
1067 t.push_back( 0 );
1068 }
1069
1070 m_Ku = std::move( ku );
1071 m_Kd = std::move( kd );
1072 m_t = std::move( t );
1073}
1074
1075
1077 const std::pair<IbisWaveform*, IbisWaveform*>& aPair1,
1078 const std::pair<IbisWaveform*, IbisWaveform*>& aPair2,
1079 const KIBIS_PARAMETER& aParam )
1080{
1081 std::string simul = "";
1082 const IBIS_CORNER supply = aParam.m_supply;
1083 const KIBIS_WAVEFORM* wave = aParam.m_waveform;
1084
1085 if( !wave )
1086 return;
1087
1088 if( wave->GetType() == KIBIS_WAVEFORM_TYPE::NONE )
1089 {
1090 //@TODO , there could be some current flowing through pullup / pulldown transistors, even when off
1091 std::vector<double> ku, kd, t;
1092 ku.push_back( 0 );
1093 kd.push_back( 0 );
1094 t.push_back( 0 );
1095 m_Ku = std::move( ku );
1096 m_Kd = std::move( kd );
1097 m_t = std::move( t );
1098 }
1099 else
1100 {
1101 simul += KuKdDriver( aModel, aPair1, aParam, 0 );
1102 simul += KuKdDriver( aModel, aPair2, aParam, 1 );
1103 simul += "\n x1 3 0 1 DRIVER0 \n";
1104
1105 simul += "VCC 3 0 ";
1106 simul += doubleToString( aModel.m_voltageRange.value[supply] );
1107 simul += "\n";
1108 //simul += "Vpin x1.DIE 0 1 \n"
1109 simul += "Lfixture0 1 4 ";
1110 simul += doubleToString( aPair1.first->m_L_fixture );
1111 simul += "\n";
1112 simul += "Rfixture0 4 5 ";
1113 simul += doubleToString( aPair1.first->m_R_fixture );
1114 simul += "\n";
1115 simul += "Cfixture0 4 0 ";
1116 simul += doubleToString( aPair1.first->m_C_fixture );
1117 simul += "\n";
1118 simul += "Vfixture0 5 0 ";
1119 simul += doubleToString( aPair1.first->m_V_fixture );
1120 simul += "\n";
1121 simul += "VmeasIout0 x1.2 x1.DIE0 0\n";
1122 simul += "VmeasPD0 0 x1.PD_GND0 0\n";
1123 simul += "VmeasPU0 x1.PU_PWR0 3 0\n";
1124 simul += "VmeasPC0 x1.PC_PWR0 3 0\n";
1125 simul += "VmeasGC0 0 x1.GC_GND0 0\n";
1126
1127
1128 simul += "\n x2 3 0 7 DRIVER1 \n";
1129 //simul += "Vpin x1.DIE 0 1 \n"
1130 simul += "Lfixture1 7 8 ";
1131 simul += doubleToString( aPair2.first->m_L_fixture );
1132 simul += "\n";
1133 simul += "Rfixture1 8 9 ";
1134 simul += doubleToString( aPair2.first->m_R_fixture );
1135 simul += "\n";
1136 simul += "Cfixture1 8 0 ";
1137 simul += doubleToString( aPair2.first->m_C_fixture );
1138 simul += "\n";
1139 simul += "Vfixture1 9 0 ";
1140 simul += doubleToString( aPair2.first->m_V_fixture );
1141 simul += "\n";
1142 simul += "VmeasIout1 x2.2 x2.DIE0 0\n";
1143 simul += "VmeasPD1 0 x2.PD_GND0 0\n";
1144 simul += "VmeasPU1 x2.PU_PWR0 3 0\n";
1145 simul += "VmeasPC1 x2.PC_PWR0 3 0\n";
1146 simul += "VmeasGC1 0 x2.GC_GND0 0\n";
1147
1148 if( aModel.HasPullup() && aModel.HasPulldown() )
1149 {
1150 simul +=
1151 "Bku KU 0 v=( ( i(VmeasPD1) * ( i(VmeasIout0) + i(VmeasPC0) + i(VmeasGC0) ) - "
1152 "i(VmeasPD0) * ( i(VmeasIout1) + i(VmeasPC1) + i(VmeasGC1) ) )/ ( i(VmeasPU1) "
1153 "* "
1154 "i(VmeasPD0) - i(VmeasPU0) * i(VmeasPD1) ) )\n";
1155 simul +=
1156 "Bkd KD 0 v=( ( i(VmeasPU1) * ( i(VmeasIout0) + i(VmeasPC0) + i(VmeasGC0) ) - "
1157 "i(VmeasPU0) * ( i(VmeasIout1) + i(VmeasPC1) + i(VmeasGC1) ) )/ ( i(VmeasPD1) "
1158 "* "
1159 "i(VmeasPU0) - i(VmeasPD0) * i(VmeasPU1) ) )\n";
1160 //simul += "Bkd KD 0 v=(1-v(KU))\n";
1161 }
1162
1163 else if( !aModel.HasPullup() && aModel.HasPulldown() )
1164 {
1165 Report( _( "There are two waveform pairs, but only one transistor. More equations than "
1166 "unknowns." ),
1168 simul += "Bku KD 0 v=( ( i(VmeasIout0)+i(VmeasPC0)+i(VmeasGC0) )/(i(VmeasPD0)))\n";
1169 simul += "Bkd KU 0 v=0\n";
1170 }
1171
1172 else if( aModel.HasPullup() && !aModel.HasPulldown() )
1173 {
1174 Report( _( "There are two waveform pairs, but only one transistor. More equations than "
1175 "unknowns." ),
1177 simul += "Bku KU 0 v=( ( i(VmeasIout)+i(VmeasPC)+i(VmeasGC) )/(i(VmeasPU)))\n";
1178 simul += "Bkd KD 0 v=0\n";
1179 }
1180 else
1181 {
1182 Report( _( "Driver needs at least a pullup or a pulldown" ), RPT_SEVERITY_ERROR );
1183 }
1184
1185 switch( wave->GetType() )
1186 {
1189 {
1190 double duration = wave->GetDuration();
1191 simul += ".tran 0.1n ";
1192 simul += doubleToString( duration );
1193 simul += "\n";
1194 break;
1195 }
1199 default:
1200 simul += ".tran 0.5 1 \n"; //
1201 }
1202
1203 //simul += ".dc Vpin -5 5 0.1\n";
1204 simul += ".control run \n";
1205 simul += "set filetype=ascii\n";
1206 simul += "run \n";
1207 simul += "plot v(KU) v(KD)\n";
1208 //simul += "plot v(x1.DIE0) \n";
1209 std::string outputFileName = m_topLevel->m_cacheDir + "temp_output.spice";
1210 simul += "write '" + outputFileName + "' v(KU) v(KD)\n";
1211 simul += "quit\n";
1212 simul += ".endc \n";
1213 simul += ".end \n";
1214
1215 getKuKdFromFile( simul );
1216 }
1217}
1218
1219
1220bool KIBIS_PIN::writeSpiceDriver( std::string& aDest, const std::string& aName, KIBIS_MODEL& aModel,
1221 const KIBIS_PARAMETER& aParam )
1222{
1223 bool status = true;
1224
1225 switch( aModel.m_type )
1226 {
1239 {
1240 std::string result;
1241 std::string tmp;
1242
1243 result = "\n*Driver model generated by Kicad using Ibis data. ";
1244
1245 result += "\n*Pin number: ";
1247 result += "\n*Signal name: ";
1249 result += "\n*Model: ";
1250 result += aModel.m_name;
1251 result += "\n.SUBCKT ";
1252 result += aName;
1253 result += " GND PIN \n";
1254 result += "\n";
1255
1256 result += "RPIN 1 PIN ";
1257 result += doubleToString( m_Rpin.value[aParam.m_Rpin] );
1258 result += "\n";
1259 result += "LPIN DIE0 1 ";
1260 result += doubleToString( m_Lpin.value[aParam.m_Lpin] );
1261 result += "\n";
1262 result += "CPIN PIN GND ";
1263 result += doubleToString( m_Cpin.value[aParam.m_Cpin] );
1264 result += "\n";
1265
1266 std::vector<std::pair<IbisWaveform*, IbisWaveform*>> wfPairs = aModel.waveformPairs();
1268
1269 if( wfPairs.size() < 1 || accuracy <= KIBIS_ACCURACY::LEVEL_0 )
1270 {
1272 {
1273 Report( _( "Model has no waveform pair, using [Ramp] instead, poor accuracy" ),
1275 }
1276
1277 getKuKdNoWaveform( aModel, aParam );
1278 }
1279 else if( wfPairs.size() == 1 || accuracy <= KIBIS_ACCURACY::LEVEL_1 )
1280 {
1281 getKuKdOneWaveform( aModel, wfPairs.at( 0 ), aParam );
1282 }
1283 else
1284 {
1285 if( wfPairs.size() > 2 || accuracy <= KIBIS_ACCURACY::LEVEL_2 )
1286 {
1287 Report( _( "Model has more than 2 waveform pairs, using the first two." ),
1289 }
1290
1291 getKuKdTwoWaveforms( aModel, wfPairs.at( 0 ), wfPairs.at( 1 ), aParam );
1292 }
1293
1294 result += "Vku KU GND pwl ( ";
1295
1296 for( size_t i = 0; i < m_t.size(); i++ )
1297 {
1298 result += doubleToString( m_t.at( i ) );
1299 result += " ";
1300 result += doubleToString( m_Ku.at( i ) );
1301 result += " ";
1302 }
1303
1304 result += ") \n";
1305
1306
1307 result += "Vkd KD GND pwl ( ";
1308
1309 for( size_t i = 0; i < m_t.size(); i++ )
1310 {
1311 result += doubleToString( m_t.at( i ) );
1312 result += " ";
1313 result += doubleToString( m_Kd.at( i ) );
1314 result += " ";
1315 }
1316
1317 result += ") \n";
1318
1319 result += aModel.SpiceDie( aParam, 0, true );
1320
1321 result += "\n.ENDS DRIVER\n\n";
1322
1323 aDest += result;
1324 break;
1325 }
1326 default:
1327 Report( _( "Invalid model type for a driver." ), RPT_SEVERITY_ERROR );
1328 status = false;
1329 }
1330
1331 return status;
1332}
1333
1334
1335bool KIBIS_PIN::writeSpiceDevice( std::string& aDest, const std::string& aName, KIBIS_MODEL& aModel,
1336 const KIBIS_PARAMETER& aParam )
1337{
1338 bool status = true;
1339
1340 switch( aModel.m_type )
1341 {
1348 {
1349 std::string result;
1350
1351 result += "\n";
1352 result = "*Device model generated by Kicad using Ibis data.";
1353 result += "\n.SUBCKT ";
1354 result += aName;
1355 result += " GND PIN\n";
1356 result += "\n";
1357 result += "\n";
1358 result += "RPIN 1 PIN ";
1359 result += doubleToString( m_Rpin.value[aParam.m_Rpin] );
1360 result += "\n";
1361 result += "LPIN DIE0 1 ";
1362 result += doubleToString( m_Lpin.value[aParam.m_Lpin] );
1363 result += "\n";
1364 result += "CPIN PIN GND ";
1365 result += doubleToString( m_Cpin.value[aParam.m_Cpin] );
1366 result += "\n";
1367
1368 result += aModel.SpiceDie( aParam, 0, false );
1369
1370 result += "\n.ENDS DEVICE\n\n";
1371
1372 aDest = std::move( result );
1373 break;
1374 }
1375 default:
1376 Report( _( "Invalid model type for a device" ), RPT_SEVERITY_ERROR );
1377 status = false;
1378 }
1379
1380 return status;
1381}
1382
1383
1384bool KIBIS_PIN::writeSpiceDiffDriver( std::string& aDest, const std::string& aName,
1385 KIBIS_MODEL& aModel, const KIBIS_PARAMETER& aParam )
1386{
1387 bool status = true;
1388 KIBIS_WAVEFORM* wave = aParam.m_waveform;
1389
1390 if( !wave )
1391 return false;
1392
1393 std::string result;
1394 result = "\n*Differential driver model generated by Kicad using Ibis data. ";
1395
1396 result += "\n.SUBCKT ";
1397 result += aName;
1398 result += " GND PIN_P PIN_N\n";
1399 result += "\n";
1400
1401 status &= writeSpiceDriver( result, aName + "_P", aModel, aParam );
1402 wave->inverted = !wave->inverted;
1403 status &= writeSpiceDriver( result, aName + "_N", aModel, aParam );
1404 wave->inverted = !wave->inverted;
1405
1406
1407 result += "\n";
1408 result += "x1 GND PIN_P " + aName + "_P \n";
1409 result += "x2 GND PIN_N " + aName + "_N \n";
1410 result += "\n";
1411
1412 result += "\n.ENDS " + aName + "\n\n";
1413
1414 if( status )
1415 aDest += result;
1416
1417 return status;
1418}
1419
1420
1421bool KIBIS_PIN::writeSpiceDiffDevice( std::string& aDest, const std::string& aName,
1422 KIBIS_MODEL& aModel, const KIBIS_PARAMETER& aParam )
1423{
1424 bool status = true;
1425
1426 std::string result;
1427 result = "\n*Differential device model generated by Kicad using Ibis data. ";
1428
1429 result += "\n.SUBCKT ";
1430 result += aName;
1431 result += " GND PIN_P PIN_N\n";
1432 result += "\n";
1433
1434 status &= writeSpiceDevice( result, aName + "_P", aModel, aParam );
1435 status &= writeSpiceDevice( result, aName + "_N", aModel, aParam );
1436
1437 result += "\n";
1438 result += "x1 GND PIN_P " + aName + "_P \n";
1439 result += "x2 GND PIN_N " + aName + "_N \n";
1440 result += "\n";
1441
1442 result += "\n.ENDS " + aName + "\n\n";
1443
1444 if( status )
1445 aDest += result;
1446
1447 return status;
1448}
1449
1450
1451KIBIS_MODEL* KIBIS::GetModel( const std::string& aName )
1452{
1453 for( KIBIS_MODEL& model : m_models )
1454 {
1455 if( model.m_name == aName )
1456 return &model;
1457 }
1458
1459 return nullptr;
1460}
1461
1462
1463KIBIS_COMPONENT* KIBIS::GetComponent( const std::string& aName )
1464{
1465 for( KIBIS_COMPONENT& cmp : m_components )
1466 {
1467 if( cmp.m_name == aName )
1468 return &cmp;
1469 }
1470
1471 return nullptr;
1472}
1473
1474
1475void KIBIS_PARAMETER::SetCornerFromString( IBIS_CORNER& aCorner, const std::string& aString )
1476{
1477 if( aString == "MIN" )
1478 aCorner = IBIS_CORNER::MIN;
1479 else if( aString == "MAX" )
1480 aCorner = IBIS_CORNER::MAX;
1481 else
1482 aCorner = IBIS_CORNER::TYP;
1483}
1484
1485
1486std::vector<std::pair<int, double>> KIBIS_WAVEFORM_STUCK_HIGH::GenerateBitSequence() const
1487{
1488 std::vector<std::pair<int, double>> bits;
1489 std::pair<int, double> bit;
1490 bit.first = inverted ? 1 : 0;
1491 bit.second = 0;
1492 return bits;
1493}
1494
1495
1496std::vector<std::pair<int, double>> KIBIS_WAVEFORM_STUCK_LOW::GenerateBitSequence() const
1497{
1498 std::vector<std::pair<int, double>> bits;
1499 std::pair<int, double> bit;
1500 bit.first = inverted ? 0 : 1;
1501 bit.second = 0;
1502 return bits;
1503}
1504
1505std::vector<std::pair<int, double>> KIBIS_WAVEFORM_HIGH_Z::GenerateBitSequence() const
1506{
1507 std::vector<std::pair<int, double>> bits;
1508 return bits;
1509}
1510
1511std::vector<std::pair<int, double>> KIBIS_WAVEFORM_RECTANGULAR::GenerateBitSequence() const
1512{
1513 std::vector<std::pair<int, double>> bits;
1514
1515 for( int i = 0; i < m_cycles; i++ )
1516 {
1517 std::pair<int, double> bit;
1518 bit.first = inverted ? 0 : 1;
1519 bit.second = ( m_ton + m_toff ) * i + m_delay;
1520 bits.push_back( bit );
1521
1522 bit.first = inverted ? 1 : 0;
1523 bit.second = ( m_ton + m_toff ) * i + m_delay + m_ton;
1524 bits.push_back( bit );
1525 }
1526
1527 return bits;
1528}
1529
1530
1531std::vector<std::pair<int, double>> KIBIS_WAVEFORM_PRBS::GenerateBitSequence() const
1532{
1533 std::vector<std::pair<int, double>> bitSequence;
1534 uint8_t polynomial = 0b1100000;
1535 //1100000 = x^7+x^6+1
1536 //10100 = x^5+x^3+1
1537 //110 = x^3+x^2+1
1538 uint8_t seed = 0x12; // Any non zero state
1539 uint8_t lfsr = seed;
1540
1541 if ( m_bitrate == 0 )
1542 return bitSequence;
1543
1544 double period = 1/m_bitrate;
1545 double t = 0;
1546
1547 wxASSERT( m_bits > 0 );
1548
1549 int bits = 0;
1550
1551 do
1552 {
1553 uint8_t lsb = lfsr & 0x01;
1554 bitSequence.emplace_back( ( static_cast<uint8_t>( inverted ) ^ lsb ? 1 : 0 ), t );
1555 lfsr = lfsr >> 1;
1556
1557 if ( lsb )
1558 lfsr ^= polynomial;
1559
1560 t += period;
1561
1562 } while ( ++bits < m_bits );
1563
1564 return bitSequence;
1565}
1566
1568 const IbisWaveform* aFallingWf ) const
1569{
1570 bool status = true;
1571
1572 if( m_cycles < 1 )
1573 {
1574 status = false;
1575 Report( _( "Number of cycles should be greater than 0." ), RPT_SEVERITY_ERROR );
1576 }
1577
1578 if( m_ton <= 0 )
1579 {
1580 status = false;
1581 Report( _( "ON time should be greater than 0." ), RPT_SEVERITY_ERROR );
1582 }
1583
1584 if( m_toff <= 0 )
1585 {
1586 status = false;
1587 Report( _( "OFF time should be greater than 0." ), RPT_SEVERITY_ERROR );
1588 }
1589
1590 if( aRisingWf )
1591 {
1592 if( m_ton < aRisingWf->m_table.m_entries.back().t )
1593 {
1594 status = false;
1595 Report( _( "Rising edge is longer than on time." ), RPT_SEVERITY_WARNING );
1596 }
1597 }
1598
1599 if( aFallingWf )
1600 {
1601 if( m_toff < aFallingWf->m_table.m_entries.back().t )
1602 {
1603 status = false;
1604 Report( _( "Falling edge is longer than off time." ), RPT_SEVERITY_WARNING );
1605 }
1606 }
1607
1608 status &= aRisingWf && aFallingWf;
1609
1610 return status;
1611}
1612
1613
1615 const dvdtTypMinMax& aFallingRp ) const
1616{
1617 bool status = true;
1618
1619 if( m_cycles < 1 )
1620 {
1621 status = false;
1622 Report( _( "Number of cycles should be greater than 0." ), RPT_SEVERITY_ERROR );
1623 }
1624
1625 if( m_ton <= 0 )
1626 {
1627 status = false;
1628 Report( _( "ON time should be greater than 0." ), RPT_SEVERITY_ERROR );
1629 }
1630
1631 if( m_toff <= 0 )
1632 {
1633 status = false;
1634 Report( _( "OFF time should be greater than 0." ), RPT_SEVERITY_ERROR );
1635 }
1636
1637 if( ( m_ton < aRisingRp.value[IBIS_CORNER::TYP].m_dt / 0.6 )
1638 || ( m_ton < aRisingRp.value[IBIS_CORNER::MIN].m_dt / 0.6 )
1639 || ( m_ton < aRisingRp.value[IBIS_CORNER::MAX].m_dt / 0.6 ) )
1640 {
1641 status = false;
1642 Report( _( "Rising edge is longer than ON time." ), RPT_SEVERITY_ERROR );
1643 }
1644
1645 if( ( m_toff < aFallingRp.value[IBIS_CORNER::TYP].m_dt / 0.6 )
1646 || ( m_toff < aFallingRp.value[IBIS_CORNER::MIN].m_dt / 0.6 )
1647 || ( m_toff < aFallingRp.value[IBIS_CORNER::MAX].m_dt / 0.6 ) )
1648 {
1649 status = false;
1650 Report( _( "Falling edge is longer than OFF time." ), RPT_SEVERITY_ERROR );
1651 }
1652
1653 return status;
1654}
1655
1656
1658 const dvdtTypMinMax& aFallingRp ) const
1659{
1660 bool status = true;
1661
1662 if( m_bitrate <= 0 )
1663 {
1664 status = false;
1665 Report( _( "Bitrate should be greater than 0." ), RPT_SEVERITY_ERROR );
1666 }
1667
1668 if( m_bits <= 0 )
1669 {
1670 status = false;
1671 Report( _( "Number of bits should be greater than 0." ), RPT_SEVERITY_ERROR );
1672 }
1673
1674 if( m_bitrate
1675 && ( ( 1 / m_bitrate ) < ( aRisingRp.value[IBIS_CORNER::TYP].m_dt / 0.6
1676 + aFallingRp.value[IBIS_CORNER::TYP].m_dt / 0.6 ) )
1677 && ( ( 1 / m_bitrate ) < ( aRisingRp.value[IBIS_CORNER::TYP].m_dt / 0.6
1678 + aFallingRp.value[IBIS_CORNER::TYP].m_dt / 0.6 ) )
1679 && ( ( 1 / m_bitrate ) < ( aRisingRp.value[IBIS_CORNER::TYP].m_dt / 0.6
1680 + aFallingRp.value[IBIS_CORNER::TYP].m_dt / 0.6 ) ) )
1681 {
1682 status = false;
1683 Report( _( "Bitrate is too high for rising / falling edges" ), RPT_SEVERITY_ERROR );
1684 }
1685
1686 return status;
1687}
1688
1690 const IbisWaveform* aFallingWf ) const
1691{
1692 bool status = true;
1693
1694 if( m_bitrate <= 0 )
1695 {
1696 status = false;
1697 Report( _( "Bitrate should be greater than 0." ), RPT_SEVERITY_ERROR );
1698 }
1699
1700 if( m_bits <= 0 )
1701 {
1702 status = false;
1703 Report( _( "Number of bits should be greater than 0." ), RPT_SEVERITY_ERROR );
1704 }
1705
1706 if( m_bitrate && aRisingWf && aFallingWf
1707 && ( ( 1 / m_bitrate ) < ( aRisingWf->m_table.m_entries.back().t
1708 + aFallingWf->m_table.m_entries.back().t ) ) )
1709 {
1710 status = false;
1711 Report( _( "Bitrate could be too high for rising / falling edges" ), RPT_SEVERITY_WARNING );
1712 }
1713
1714 return status;
1715}
void Report(const std::string &aMsg, SEVERITY aSeverity=RPT_SEVERITY_INFO) const
Print a message.
Definition ibis_parser.h:68
IBIS_BASE(REPORTER *aReporter)
Definition ibis_parser.h:56
REPORTER * m_Reporter
Definition ibis_parser.h:75
static std::string doubleToString(double aNumber)
Convert a double to string using scientific notation.
std::string Spice(int aN, const std::string &aPort1, const std::string &aPort2, bool aNegateI, const std::string &aModelName, IBIS_CORNER aCorner) const
Interpolate the IV table.
TypMinMaxValue m_Cpkg
TypMinMaxValue m_Lpkg
TypMinMaxValue m_Rpkg
std::string m_signalName
std::string m_pinName
std::string m_manufacturer
IbisDiffPin m_diffPin
std::string m_name
std::vector< IbisComponentPin > m_pins
std::string pinB
std::string pinA
std::vector< IbisDiffPinEntry > m_entries
std::vector< IbisComponent > m_components
IbisHeader m_header
std::map< std::string, IbisSubmodel > m_submodels
std::vector< IbisModel > m_models
std::vector< IbisModelSelector > m_modelSelectors
std::string m_fileName
std::string m_copyright
std::string m_notes
double m_ibisVersion
std::string m_date
double m_fileRevision
std::string m_disclaimer
std::string m_modelDescription
std::vector< IbisModelSelectorEntry > m_models
TypMinMaxValue m_C_comp_pullup
TypMinMaxValue m_C_comp_power_clamp
IBIS_MODEL_TYPE m_type
double m_cref
IVtable m_pullup
TypMinMaxValue m_Cac
TypMinMaxValue m_pullupReference
TypMinMaxValue m_C_comp_pulldown
TypMinMaxValue m_C_comp_gnd_clamp
double m_vmeas
IVtable m_pulldown
TypMinMaxValue m_Rac
std::vector< IbisSubmodelMode > m_submodels
std::vector< IbisWaveform * > m_risingWaveforms
IVtable m_POWERClamp
TypMinMaxValue m_Rgnd
TypMinMaxValue m_POWERClampReference
IVtable m_GNDClamp
IbisRamp m_ramp
IBIS_MODEL_POLARITY m_polarity
TypMinMaxValue m_C_comp
double m_rref
IBIS_MODEL_ENABLE m_enable
TypMinMaxValue m_temperatureRange
std::vector< IbisWaveform * > m_fallingWaveforms
TypMinMaxValue m_pulldownReference
double m_vref
TypMinMaxValue m_GNDClampReference
double m_vinh
double m_vinl
TypMinMaxValue m_voltageRange
TypMinMaxValue m_Rpower
std::string m_name
bool ParseFile(const std::string &aFileName)
Parse a file.
IbisFile m_ibisFile
dvdtTypMinMax m_rising
dvdtTypMinMax m_falling
IBIS_SUBMODEL_MODE m_mode
std::string m_name
TypMinMaxValue m_VtriggerR
IVtable m_POWERClamp
IBIS_SUBMODEL_TYPE m_type
IVtable m_GNDClamp
std::string m_name
TypMinMaxValue m_VtriggerF
double m_L_fixture
double m_V_fixture
VTtable m_table
double m_V_fixture_min
double m_V_fixture_max
double m_R_fixture
double m_C_fixture
bool m_valid
Definition kibis.h:57
KIBIS * m_topLevel
Definition kibis.h:56
KIBIS_BASE(KIBIS *aTopLevel)
Definition kibis.cpp:68
KIBIS_COMPONENT(KIBIS &aToplevel, const IbisComponent &aSource, IbisParser &aParser)
Definition kibis.cpp:366
std::vector< KIBIS_PIN > m_pins
Definition kibis.h:477
std::string m_manufacturer
Name of the manufacturer.
Definition kibis.h:475
std::string m_name
Name of the component.
Definition kibis.h:473
KIBIS_PIN * GetPin(const std::string &aPinNumber)
Get a pin by its number ( 1, 2, A1, A2, ... )
Definition kibis.cpp:390
KIBIS_FILE(KIBIS &aTopLevel)
Definition kibis.cpp:137
std::string m_notes
Definition kibis.h:238
double m_ibisVersion
Definition kibis.h:235
std::string m_date
Definition kibis.h:236
bool Init(const IbisParser &aParser)
Definition kibis.cpp:145
std::string m_fileName
Definition kibis.h:233
std::string m_copyright
Definition kibis.h:240
std::string m_disclaimer
Definition kibis.h:239
double m_fileRev
Definition kibis.h:234
std::vector< IbisWaveform * > m_fallingWaveforms
Definition kibis.h:300
IbisWaveform TrimWaveform(const IbisWaveform &aIn) const
Copy a waveform, and substract the first value to all samples.
Definition kibis.cpp:530
IBIS_MODEL_ENABLE m_enable
Definition kibis.h:280
double m_vref
Definition kibis.h:276
std::string m_name
Definition kibis.h:269
bool HasPOWERClamp() const
Return true if the model has a clamp diode to the power net.
Definition kibis.cpp:584
double m_vmeas
Definition kibis.h:279
std::vector< IbisWaveform * > m_risingWaveforms
Definition kibis.h:299
TypMinMaxValue m_Cac
Definition kibis.h:294
double m_vinh
Definition kibis.h:275
double m_rref
Definition kibis.h:277
bool HasPullup() const
Return true if the model has a pullup transistor.
Definition kibis.cpp:572
bool HasGNDClamp() const
Return true if the model has a clamp diode to the gnd net.
Definition kibis.cpp:578
std::string SpiceDie(const KIBIS_PARAMETER &aParam, int aIndex, bool aDriver) const
Generate the spice directive to simulate the die.
Definition kibis.cpp:428
TypMinMaxValue m_pulldownReference
Definition kibis.h:288
TypMinMaxValue m_GNDClampReference
Definition kibis.h:289
TypMinMaxValue m_Rpower
Definition kibis.h:292
bool HasPulldown() const
Return true if the model has a pulldown transistor.
Definition kibis.cpp:566
IBIS_MODEL_TYPE m_type
Definition kibis.h:271
std::vector< KIBIS_SUBMODEL > m_submodels
Definition kibis.h:302
IbisRamp m_ramp
Definition kibis.h:301
IVtable m_pullup
Definition kibis.h:297
IBIS_MODEL_POLARITY m_polarity
Definition kibis.h:281
double m_cref
Definition kibis.h:278
std::string generateSquareWave(const std::string &aNode1, const std::string &aNode2, const std::vector< std::pair< int, double > > &aBits, const std::pair< IbisWaveform *, IbisWaveform * > &aPair, const KIBIS_PARAMETER &aParam)
Generate a square waveform.
Definition kibis.cpp:590
TypMinMaxValue m_Rac
Definition kibis.h:293
TypMinMaxValue m_temperatureRange
Definition kibis.h:286
TypMinMaxValue m_voltageRange
Definition kibis.h:285
IVtable m_GNDClamp
Definition kibis.h:295
TypMinMaxValue m_Rgnd
Definition kibis.h:291
std::string m_description
Definition kibis.h:270
KIBIS_MODEL(KIBIS &aTopLevel, const IbisModel &aSource, IbisParser &aParser)
Definition kibis.cpp:263
IVtable m_POWERClamp
Definition kibis.h:296
TypMinMaxValue m_pullupReference
Definition kibis.h:287
std::vector< std::pair< IbisWaveform *, IbisWaveform * > > waveformPairs()
Create waveform pairs.
Definition kibis.cpp:401
TypMinMaxValue m_POWERClampReference
Definition kibis.h:290
TypMinMaxValue m_C_comp
Definition kibis.h:284
IVtable m_pulldown
Definition kibis.h:298
double m_vinl
Definition kibis.h:274
IBIS_CORNER m_supply
Definition kibis.h:220
IBIS_CORNER m_Cpin
Definition kibis.h:218
KIBIS_ACCURACY m_accuracy
Definition kibis.h:222
IBIS_CORNER m_Ccomp
Definition kibis.h:219
IBIS_CORNER m_Lpin
Definition kibis.h:217
IBIS_CORNER m_Rpin
Definition kibis.h:216
void SetCornerFromString(IBIS_CORNER &aCorner, const std::string &aString)
Definition kibis.cpp:1475
KIBIS_WAVEFORM * m_waveform
Definition kibis.h:221
TypMinMaxValue m_Rpin
Resistance from die to pin.
Definition kibis.h:376
bool writeSpiceDiffDriver(std::string &aDest, const std::string &aName, KIBIS_MODEL &aModel, const KIBIS_PARAMETER &aParam)
Definition kibis.cpp:1384
TypMinMaxValue m_Lpin
Inductance from die to pin.
Definition kibis.h:378
void getKuKdFromFile(const std::string &aSimul)
Update m_Ku, m_Kd using with two waveform inputs.
Definition kibis.cpp:735
KIBIS_COMPONENT * m_parent
Definition kibis.h:382
std::vector< double > m_Ku
Definition kibis.h:384
std::string m_pinNumber
Pin Number Examples : 1, 2, 3 ( or for BGA ), A1, A2, A3, etc...
Definition kibis.h:373
std::string addDie(KIBIS_MODEL &aModel, const KIBIS_PARAMETER &aParam, int aIndex)
Generate the spice directive to simulate the die for Ku/Kd estimation.
Definition kibis.cpp:691
bool writeSpiceDevice(std::string &aDest, const std::string &aName, KIBIS_MODEL &aModel, const KIBIS_PARAMETER &aParam)
Definition kibis.cpp:1335
std::vector< double > m_t
Definition kibis.h:384
TypMinMaxValue m_Cpin
Capacitance from pin to GND.
Definition kibis.h:380
std::string m_signalName
Name of the pin Examples : "VCC", "GPIOA", "CLK", etc...
Definition kibis.h:369
void getKuKdTwoWaveforms(KIBIS_MODEL &aModel, const std::pair< IbisWaveform *, IbisWaveform * > &aPair1, const std::pair< IbisWaveform *, IbisWaveform * > &aPair2, const KIBIS_PARAMETER &aParam)
Update m_Ku, m_Kd using with two waveform inputs.
Definition kibis.cpp:1076
void getKuKdOneWaveform(KIBIS_MODEL &aModel, const std::pair< IbisWaveform *, IbisWaveform * > &aPair, const KIBIS_PARAMETER &aParam)
Update m_Ku, m_Kd using with a single waveform input.
Definition kibis.cpp:904
bool writeSpiceDiffDevice(std::string &aDest, const std::string &aName, KIBIS_MODEL &aModel, const KIBIS_PARAMETER &aParam)
Definition kibis.cpp:1421
std::string KuKdDriver(KIBIS_MODEL &aModel, const std::pair< IbisWaveform *, IbisWaveform * > &aPair, const KIBIS_PARAMETER &aParam, int aIndex)
Update m_Ku, m_Kd using with two waveform inputs.
Definition kibis.cpp:828
KIBIS_PIN(KIBIS &aTopLevel, const IbisComponentPin &aPin, const IbisComponentPackage &aPackage, IbisParser &aParser, KIBIS_COMPONENT *aParent, std::vector< KIBIS_MODEL > &aModels)
Definition kibis.cpp:162
std::vector< double > m_Kd
Definition kibis.h:384
bool writeSpiceDriver(std::string &aDest, const std::string &aName, KIBIS_MODEL &aModel, const KIBIS_PARAMETER &aParam)
Definition kibis.cpp:1220
KIBIS_PIN * m_complementaryPin
Definition kibis.h:463
void getKuKdNoWaveform(KIBIS_MODEL &aModel, const KIBIS_PARAMETER &aParam)
Update m_Ku, m_Kd using no falling / rising waveform inputs ( low accuracy )
Definition kibis.cpp:1015
KIBIS_SUBMODEL(KIBIS &aTopLevel, const IbisSubmodel &aSource, IBIS_SUBMODEL_MODE aMode)
Definition kibis.cpp:236
IVtable m_GNDClamp
Definition kibis.h:255
std::string m_name
Definition kibis.h:250
bool HasPOWERClamp() const
Return true if the model has a clamp diode to the power net.
Definition kibis.cpp:257
IBIS_SUBMODEL_MODE m_mode
Definition kibis.h:252
bool HasGNDClamp() const
Return true if the model has a clamp diode to the gnd net.
Definition kibis.cpp:251
IVtable m_POWERClamp
Definition kibis.h:256
IBIS_SUBMODEL_TYPE m_type
Definition kibis.h:251
std::vector< std::pair< int, double > > GenerateBitSequence() const override
Definition kibis.cpp:1505
bool Check(const IbisWaveform *aRisingWf, const IbisWaveform *aFallingWf) const override
Definition kibis.cpp:1689
std::vector< std::pair< int, double > > GenerateBitSequence() const override
Definition kibis.cpp:1531
bool Check(const IbisWaveform *aRisingWf, const IbisWaveform *aFallingWf) const override
Definition kibis.cpp:1567
std::vector< std::pair< int, double > > GenerateBitSequence() const override
Definition kibis.cpp:1511
std::vector< std::pair< int, double > > GenerateBitSequence() const override
Definition kibis.cpp:1486
std::vector< std::pair< int, double > > GenerateBitSequence() const override
Definition kibis.cpp:1496
virtual bool Check(const IbisWaveform *aRisingWf, const IbisWaveform *aFallingWf) const
Definition kibis.h:92
KIBIS_WAVEFORM_TYPE GetType() const
Definition kibis.h:80
bool inverted
Definition kibis.h:82
virtual double GetDuration() const
Definition kibis.h:81
virtual std::vector< std::pair< int, double > > GenerateBitSequence() const
Definition kibis.h:85
Definition kibis.h:488
std::vector< KIBIS_MODEL > m_models
Definition kibis.h:499
KIBIS()
Constructor for unitialized KIBIS members.
Definition kibis.h:491
std::vector< KIBIS_COMPONENT > m_components
Definition kibis.h:498
KIBIS_MODEL * GetModel(const std::string &aName)
Return the model with name aName .
Definition kibis.cpp:1451
KIBIS_COMPONENT * GetComponent(const std::string &aName)
Return the component with name aName .
Definition kibis.cpp:1463
KIBIS_FILE m_file
Definition kibis.h:500
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
static std::shared_ptr< SPICE_SIMULATOR > CreateInstance(const std::string &aName)
double value[3]
bool isNA() const
TypMinMaxValue V
std::vector< VTtableEntry > m_entries
double m_dt
#define _(s)
IBIS_CORNER
@ TYP
@ MIN
@ MAX
IBIS_SUBMODEL_MODE
std::vector< std::pair< int, double > > SimplifyBitSequence(const std::vector< std::pair< int, double > > &bits)
Definition kibis.cpp:51
IBIS_CORNER ReverseLogic(IBIS_CORNER aIn)
Definition kibis.cpp:82
KIBIS_ACCURACY
Accuracy level.
Definition kibis.h:206
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
KIBIS_MODEL * model
KIBIS_PIN * pin
const int accuracy
wxString result
Test unit parsing edge cases and error handling.
int delta
#define ku