21#include <boost/test/results_collector.hpp>
32#include <wx/tokenzr.h>
33#include <wx/filename.h>
44 const wxString& aVariant )
51 const wxString& aVariant )
65 wxStringTokenizer lines( aNetlist, wxS(
"\n" ), wxTOKEN_RET_EMPTY_ALL );
67 while( lines.HasMoreTokens() )
69 wxString line = lines.GetNextToken();
71 if( line.StartsWith( wxS(
".include " ) ) )
73 wxFileName fn( line.AfterFirst(
'"' ).BeforeLast(
'"' ) );
74 line = wxS(
".include \"" ) + fn.GetFullName() + wxS(
"\"" );
77 out << line << wxS(
"\n" );
87 std::vector<wxString> tokens;
88 wxStringTokenizer tokenizer( aLine, wxS(
" \t" ), wxTOKEN_STRTOK );
90 while( tokenizer.HasMoreTokens() )
91 tokens.push_back( tokenizer.GetNextToken() );
108 if( symbol->
GetRef( &sheet ) == aRef )
130 TestNetlist(
"rectifier" );
131 TestTranPoint( 0, { {
"V(/in)", 0 }, {
"V(/out)", 0 } } );
132 TestTranPoint( 9250e-6, { {
"V(/in)", 5 }, {
"V(/out)", 4.26 } } );
133 TestTranPoint( 10e-3, { {
"V(/in)", 0 }, {
"V(/out)", 4.24 } } );
154 TestNetlist(
"opamp" );
155 TestTranPoint( 0, { {
"V(/in)", 0 }, {
"V(/out)", 0 } } );
156 TestTranPoint( 250e-6, { {
"V(/in)", 500e-3 }, {
"V(/out)", 1 } } );
157 TestTranPoint( 500e-6, { {
"V(/in)", 0 }, {
"V(/out)", 0 } } );
158 TestTranPoint( 750e-6, { {
"V(/in)", -500e-3 }, {
"V(/out)", -1 } } );
159 TestTranPoint( 1e-3, { {
"V(/in)", 0 }, {
"V(/out)", 0 } } );
168 TestNetlist(
"npn_ce_amp" );
169 TestTranPoint( 900e-6, { {
"V(/in)", 0 }, {
"V(/out)", 5.32 } } );
170 TestTranPoint( 925e-6, { {
"V(/in)", 10e-3 }, {
"V(/out)", 5.30 } } );
171 TestTranPoint( 950e-6, { {
"V(/in)", 0 }, {
"V(/out)", 5.88 } } );
172 TestTranPoint( 975e-6, { {
"V(/in)", -10e-3 }, {
"V(/out)", 5.91 } } );
173 TestTranPoint( 1e-3, { {
"V(/in)", 0 }, {
"V(/out)", 5.32 } } );
180 TestNetlist(
"rlc" );
181 TestTranPoint( 9.43e-3, { {
"V(/Vp)", -19e-3 }, {
"I(Rs1)", 19e-3 } } );
182 TestTranPoint( 9.74e-3, { {
"V(/Vp)", 19e-3 }, {
"I(Rs1)", -19e-3 } } );
188 TestNetlist(
"potentiometers" );
189 TestOpPoint( 0.5,
"V(/out1)" );
190 TestOpPoint( 0.7,
"V(/out2)" );
191 TestOpPoint( 0.9,
"V(/out3)" );
198 TestNetlist(
"tlines" );
199 TestTranPoint( 910e-6, { {
"V(/z0_in)", 1 }, {
"V(/z0_out)", 0 },
200 {
"V(/rlgc_in)", 1 }, {
"V(/rlgc_out)", 0 } } );
201 TestTranPoint( 970e-6, { {
"V(/z0_in)", 0 }, {
"V(/z0_out)", 1 },
202 {
"V(/rlgc_in)", 0 }, {
"V(/rlgc_out)", 1 } } );
229 TestNetlist(
"cmos_not" );
230 TestTranPoint( 0, { {
"V(/in)", 2.5 }, {
"V(/out)", 2.64 } } );
231 TestTranPoint( 250e-6, { {
"V(/in)", 5 }, {
"V(/out)", 0.013 } } );
232 TestTranPoint( 500e-6, { {
"V(/in)", 2.5 }, {
"V(/out)", 2.64 } } );
233 TestTranPoint( 750e-6, { {
"V(/in)", 0 }, {
"V(/out)", 5 } } );
234 TestTranPoint( 1e-3, { {
"V(/in)", 2.5 }, {
"V(/out)", 2.64 } } );
251 TestNetlist(
"fliege_filter" );
252 TestACPoint( 0.8e3, { {
"V(/in)", 1 }, {
"V(/out)", 1 } } );
253 TestACPoint( 1.061e3, { {
"V(/in)", 1 }, {
"V(/out)", 0 } } );
254 TestACPoint( 1.2e3, { {
"V(/in)", 1 }, {
"V(/out)", 1 } } );
261 TestNetlist(
"switches" );
262 TestTranPoint( 0.5e-3, { {
"V(/inswv)", 0 }, {
"V(/outswv)", 0 } } );
263 TestTranPoint( 1.5e-3, { {
"V(/inswv)", 1 }, {
"V(/outswv)", 5 } } );
264 TestTranPoint( 2.5e-3, { {
"V(/inswv)", 0 }, {
"V(/outswv)", 0 } } );
276 TestNetlist(
"directives" );
277 TestTranPoint( 9.25e-3, { {
"V(/in)", 1 }, {
"V(/out)", -900e-3 }, {
"I(XR1)", 1e-3 } } );
278 TestTranPoint( 9.50e-3, { {
"V(/in)", 0 }, {
"V(/out)", 0 }, {
"I(XR1)", 1e-3 } } );
279 TestTranPoint( 9.75e-3, { {
"V(/in)", -1 }, {
"V(/out)", 900e-3 }, {
"I(XR1)", 1e-3 } } );
280 TestTranPoint( 10e-3, { {
"V(/in)", 0 }, {
"V(/out)", 0 }, {
"I(XR1)", 1e-3 } } );
292 TestNetlist(
"legacy_laser_driver" );
298 TestTranPoint( 95e-9, { {
"I(D1)", 0 } } );
300 TestTranPoint( 110e-9, { {
"I(D1)", 0.770 } }, 0.1 );
302 TestTranPoint( 150e-9, { {
"I(D1)", 0 } } );
308 TestNetlist(
"legacy_pot" );
309 TestOpPoint( 0.5,
"V(/out1)" );
310 TestOpPoint( 0.7,
"V(/out2)" );
311 TestOpPoint( 0.9,
"V(/out3)" );
317 TestNetlist(
"legacy_pspice" );
318 TestACPoint( 190, { {
"V(/VIN)", pow( 10, -186e-3 / 20 ) },
319 {
"V(VOUT)", pow( 10, 87e-3 / 20 ) } } );
326 TestNetlist(
"legacy_rectifier" );
327 TestTranPoint( 0, { {
"V(/signal_in)", 0 },
328 {
"V(/rect_out)", 0 } } );
329 TestTranPoint( 9.75e-3, { {
"V(/signal_in)", 1.5 },
330 {
"V(/rect_out)", 823e-3 } } );
337 TestNetlist(
"legacy_sallen_key" );
342 TestACPoint( 1, { {
"V(/lowpass)", pow( 10, 0.0 / 20 ) } } );
343 TestACPoint( 1e3, { {
"V(/lowpass)", pow( 10, -2.9 / 20 ) } } );
367 TestNetlist(
"legacy_opamp" );
368 TestTranPoint( 0, { {
"V(/in)", 0 }, {
"V(/out)", 0 } } );
369 TestTranPoint( 250e-6, { {
"V(/in)", 500e-3 }, {
"V(/out)", 1 } } );
370 TestTranPoint( 500e-6, { {
"V(/in)", 0 }, {
"V(/out)", 0 } } );
371 TestTranPoint( 750e-6, { {
"V(/in)", -500e-3 }, {
"V(/out)", -1 } } );
372 TestTranPoint( 1e-3, { {
"V(/in)", 0 }, {
"V(/out)", 0 } } );
384 LoadSchematic( SchematicQAPath(
"multiunit_pinmap_split" ) );
387 wxString netlistPath = GetNetlistPath(
true );
388 wxFFile netlistFile( netlistPath,
"rt" );
392 netlistFile.ReadAll( &
netlist );
398 wxStringTokenizer lines(
netlist, wxS(
"\n" ) );
400 while( lines.HasMoreTokens() )
402 wxString line = lines.GetNextToken();
404 if( line.StartsWith( wxS(
"XU1 " ) ) )
406 BOOST_REQUIRE_MESSAGE( xu1Line.IsEmpty(),
407 "Multiple XU1 lines found; multi-unit symbols must emit a "
408 "single subcircuit instance, not one per unit" );
413 BOOST_REQUIRE_MESSAGE( !xu1Line.IsEmpty(),
414 "XU1 subcircuit instance was not generated" );
416 wxStringTokenizer tokens( xu1Line, wxS(
" \t" ), wxTOKEN_STRTOK );
417 std::vector<wxString> parts;
419 while( tokens.HasMoreTokens() )
420 parts.push_back( tokens.GetNextToken() );
427 const std::vector<wxString> expectedParts = {
429 wxS(
"Net-_U1C-V+_" ),
430 wxS(
"Net-_U1C-V-_" ),
431 wxS(
"Net-_U1A-+_" ),
432 wxS(
"Net-_U1A--_" ),
434 wxS(
"Net-_U1B-+_" ),
435 wxS(
"Net-_U1A--_" ),
436 wxS(
"Net-_C2-Pad1_" ),
437 wxS(
"uopamp_lvl2_2x" )
440 BOOST_REQUIRE_EQUAL( parts.size(), expectedParts.size() );
442 for(
size_t ii = 0; ii < expectedParts.size(); ++ii )
445 "Expected XU1 token " << ii <<
" to be '" << expectedParts[ii]
446 <<
"', got '" << parts[ii]
447 <<
"'. Per-unit Sim.Pins merge is missing or incorrect" );
461 LoadSchematic( SchematicQAPath( wxS(
"multiunit_repeat_opamp" ) ) );
464 wxString netlistPath = GetNetlistPath(
true );
465 wxFFile netlistFile( netlistPath,
"rt" );
469 netlistFile.ReadAll( &
netlist );
476 std::vector<wxString> innerInstanceLines;
477 bool hasBaseInclude =
false;
479 wxStringTokenizer lines(
netlist, wxS(
"\n" ) );
481 while( lines.HasMoreTokens() )
483 wxString line = lines.GetNextToken();
485 if( line.StartsWith( wxS(
"XU1 " ) ) )
487 BOOST_REQUIRE_MESSAGE( xu1Line.IsEmpty(),
"multiple XU1 lines; expected exactly one" );
490 else if( line.StartsWith( wxS(
".subckt kicad_mu_" ) ) )
492 BOOST_REQUIRE_MESSAGE( subcktLine.IsEmpty(),
493 "wrapper .subckt emitted more than once" );
496 else if( line.StartsWith( wxS(
"X" ) ) && line.EndsWith( wxS(
"uopamp_single" ) ) )
498 innerInstanceLines.push_back( line );
500 else if( line.Contains( wxS(
"uopamp_single.lib.spice" ) )
501 && line.StartsWith( wxS(
".include" ) ) )
503 hasBaseInclude =
true;
507 BOOST_REQUIRE_MESSAGE( !xu1Line.IsEmpty(),
"outer XU1 line was not generated" );
508 BOOST_REQUIRE_MESSAGE( !subcktLine.IsEmpty(),
"wrapper .subckt was not generated" );
512 BOOST_REQUIRE_GE( subcktTokens.size(), 2u );
513 wxString wrapperName = subcktTokens[1];
517 BOOST_REQUIRE_GE( xu1Tokens.size(), 2u );
521 BOOST_REQUIRE_EQUAL( innerInstanceLines.size(), 2u );
528 BOOST_REQUIRE_EQUAL( x1.size(), 7u );
529 BOOST_REQUIRE_EQUAL( x2.size(), 7u );
535 const std::vector<wxString> expectedOuterNets = {
536 wxS(
"Net-_U1A-+_" ),
537 wxS(
"Net-_U1A--_" ),
539 wxS(
"Net-_U1B-+_" ),
540 wxS(
"Net-_U1A--_" ),
541 wxS(
"Net-_C2-Pad1_" ),
542 wxS(
"Net-_U1C-V+_" ),
543 wxS(
"Net-_U1C-V-_" )
546 std::vector<wxString> outerNets( xu1Tokens.begin() + 1, xu1Tokens.end() - 1 );
547 BOOST_CHECK( outerNets == expectedOuterNets );
560 LoadSchematic( SchematicQAPath( wxS(
"multiunit_repeat_opamp" ) ) );
577 LoadSchematic( SchematicQAPath( wxS(
"multiunit_repeat_opamp" ) ) );
585 exporter, *primary, primarySheet, wxEmptyString );
587 BOOST_REQUIRE_EQUAL( maps.size(), 3 );
593 const std::vector<std::pair<wxString, wxString>> unit1 = {
594 { wxS(
"1" ), wxS(
"OUT" ) }, { wxS(
"2" ), wxS(
"-IN" ) }, { wxS(
"3" ), wxS(
"+IN" ) } };
595 const std::vector<std::pair<wxString, wxString>> unit2 = {
596 { wxS(
"5" ), wxS(
"+IN" ) }, { wxS(
"6" ), wxS(
"-IN" ) }, { wxS(
"7" ), wxS(
"OUT" ) } };
597 const std::vector<std::pair<wxString, wxString>> unit3 = {
598 { wxS(
"4" ), wxS(
"VEE" ) }, { wxS(
"8" ), wxS(
"VCC" ) } };
600 BOOST_CHECK( maps[0].pins == unit1 );
601 BOOST_CHECK( maps[1].pins == unit2 );
602 BOOST_CHECK( maps[2].pins == unit3 );
613 LoadSchematic( SchematicQAPath( wxS(
"multiunit_repeat_opamp" ) ) );
621 exporter, *primary, primarySheet, wxEmptyString );
639 const std::vector<wxString> fixtures = { wxS(
"multiunit_pinmap_split" ),
640 wxS(
"fliege_filter" ) };
642 for(
const wxString& fixture : fixtures )
649 wxString netlistPath = GetNetlistPath(
true );
650 wxFFile netlistFile( netlistPath,
"rt" );
654 netlistFile.ReadAll( &
netlist );
659 wxFileName goldenFn = SchematicQAPath( fixture );
660 goldenFn.SetExt(
"golden" );
661 wxString goldenPath = goldenFn.GetFullPath();
663 if( wxGetEnv( wxS(
"KICAD_RECORD_GOLDEN" ),
nullptr ) )
665 wxFFile out( goldenPath,
"wt" );
667 out.Write( normalized );
673 wxFFile goldenFile( goldenPath,
"rt" );
674 BOOST_REQUIRE_MESSAGE( goldenFile.IsOpened(),
675 "Missing golden netlist; regenerate with "
676 "KICAD_RECORD_GOLDEN=1" );
679 goldenFile.ReadAll( &golden );
700 const std::vector<wxString> fixtures = { wxS(
"opamp" ), wxS(
"npn_ce_amp" ) };
702 for(
const wxString& fixture : fixtures )
713 wxString
path = GetNetlistPath(
true );
718 wxFFile file(
path,
"rt" );
728 wxString first = exportOnce();
729 wxString second = exportOnce();
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
static SIM_DECOMPOSITION GetDecomposition(NETLIST_EXPORTER_SPICE &aExporter, SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aSheet, const wxString &aVariant)
static std::vector< UNIT_PIN_MAP > CollectUnitPinMaps(NETLIST_EXPORTER_SPICE &aExporter, SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aSheet, const wxString &aVariant)
SIM_DECOMPOSITION getDecomposition(SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aSheet, const wxString &aVariantName) const
Read and parse the Sim.Decomposition field from the primary unit.
bool WriteNetlist(const wxString &aOutFileName, unsigned aNetlistOptions, REPORTER &aReporter) override
Write to specified output file.
std::vector< UNIT_PIN_MAP > collectUnitPinMaps(SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aSheet, const wxString &aVariantName)
Gather every unit's Sim.Pins mapping for a multi-unit symbol, one entry per unit.
Holds all the data relating to one schematic.
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
A wrapper for reporting to a wxString object.
static void LoadSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< FAB_LAYER_COLOR > dummy
Per-component decomposition descriptor stored in the Sim.Decomposition field.
std::vector< wxString > sharedModelPins
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
BOOST_TEST_INFO("Two-port Series .op current = "<< iDevice)
BOOST_AUTO_TEST_CASE(Rectifier)
static SCH_SYMBOL * findPrimaryUnit(SCHEMATIC *aSchematic, const wxString &aRef, SCH_SHEET_PATH &aSheetOut)
static std::vector< wxString > splitNetlistLine(const wxString &aLine)
static wxString normalizeSpiceNetlist(const wxString &aNetlist)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_CHECK_EQUAL(result, "25.4")