KiCad PCB EDA Suite
Loading...
Searching...
No Matches
netlist_exporter_base.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) 1992-2017 jp.charras at wanadoo.fr
5 * Copyright (C) 2013-2017 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
23
24#include <string_utils.h>
25
26#include <trace_helpers.h>
27#include <connection_graph.h>
28#include <kiface_ids.h>
29#include <kiway.h>
30#include <lib_id.h>
31#include <sch_pin.h>
32#include <sch_reference_list.h>
33#include <sch_screen.h>
34#include <sch_symbol.h>
35#include <schematic.h>
36#include <advanced_config.h>
38#include <reporter.h>
39#include <richio.h>
40
41
43 m_exporter( aExporter )
44{
45 if( m_exporter.m_connectivityDepth == 0 )
46 m_exporter.rebuildConnectivity();
47
48 ++m_exporter.m_connectivityDepth;
49}
50
55
56bool NETLIST_EXPORTER_BASE::WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
57 REPORTER& aReporter )
58{
59 try
60 {
61 CONNECTIVITY_SCOPE connectivity( *this );
62 return writeNetlist( aOutFileName, aNetlistOptions, aReporter );
63 }
64 catch( const IO_ERROR& error )
65 {
66 aReporter.Report( error.What(), RPT_SEVERITY_ERROR );
67 return false;
68 }
69 catch( const std::exception& error )
70 {
71 aReporter.Report( wxString::Format( _( "Failed to export schematic: %s" ), error.what() ),
73 return false;
74 }
75}
76
78{
79 m_exportNets.clear();
80 m_exportItemNets.clear();
81 m_schematic->RebuildConnectivity();
82 m_exportSheets = m_schematic->Hierarchy();
83
84 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
85 {
86 auto& facade = m_schematic->Connectivity();
87 std::map<KIID_PATH, const SCH_SHEET_PATH*> paths;
88
89 for( const SCH_SHEET_PATH& path : m_exportSheets )
90 paths.emplace( path.Path(), &path );
91
92 for( const auto& [key, row] : facade.Published().Rows() )
93 {
94 const auto& path = facade.Keys().Instance( key.inst );
95 const auto view = facade.Connection( key.item, path );
96
97 if( !view )
98 THROW_IO_ERROR( _( "Connectivity changed while preparing the netlist." ) );
99
100 const auto sheet = paths.find( path );
101
102 if( sheet == paths.end() )
103 THROW_IO_ERROR( _( "Connectivity contains an unknown sheet instance." ) );
104
105 m_exportItemNets[*sheet->second].emplace( key.item, view->Name() );
106 }
107
108 for( const SCH_CONNECTIVITY::NET_GROUP& group : facade.GetNetMap() )
109 {
110 EXPORT_NET result{ group.name, false, {} };
111
112 for( const SCH_CONNECTIVITY::NET_VIEW& net : group.instances )
113 {
114 if( !net.IsNet() )
115 continue;
116
117 const auto path = paths.find( net.Instance() );
118
119 if( path == paths.end() )
120 THROW_IO_ERROR( _( "Connectivity contains an unknown sheet instance." ) );
121
122 for( SCH_ITEM* item : net.Items() )
123 {
124 result.hasNoConnect |= item->Type() == SCH_NO_CONNECT_T;
125
126 if( item->Type() == SCH_PIN_T )
127 result.pins.emplace_back( static_cast<SCH_PIN*>( item ), *path->second );
128 }
129 }
130
131 m_exportNets.push_back( std::move( result ) );
132 }
133
134 return;
135 }
136
137 for( const SCH_SHEET_PATH& path : m_exportSheets )
138 {
139 std::unordered_map<KIID, wxString>& itemNets = m_exportItemNets[path];
140
141 auto collect =
142 [&]( SCH_ITEM* item )
143 {
144 if( SCH_CONNECTION* connection = item->Connection( &path ) )
145 itemNets.emplace( item->m_Uuid, connection->Name() );
146 };
147
148 for( SCH_ITEM* item : path.LastScreen()->Items() )
149 {
150 collect( item );
151 item->RunOnChildren( collect, RECURSE_MODE::NO_RECURSE );
152 }
153 }
154
155 for( const auto& [key, subgraphs] : m_schematic->ConnectionGraph()->GetNetMap() )
156 {
157 EXPORT_NET result{ key.Name, false, {} };
158
159 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
160 {
161 result.hasNoConnect |= subgraph->GetNoConnect()
162 && subgraph->GetNoConnect()->Type() == SCH_NO_CONNECT_T;
163 const SCH_SHEET_PATH& path = subgraph->GetSheet();
164
165 for( SCH_ITEM* item : subgraph->GetItems() )
166 {
167 if( item->Type() == SCH_PIN_T )
168 result.pins.emplace_back( static_cast<SCH_PIN*>( item ), path );
169 }
170 }
171
172 m_exportNets.push_back( std::move( result ) );
173 }
174}
175
176std::optional<wxString> NETLIST_EXPORTER_BASE::itemNetName( const SCH_ITEM& aItem,
177 const SCH_SHEET_PATH& aPath ) const
178{
179 const auto sheet = m_exportItemNets.find( aPath );
180
181 if( sheet == m_exportItemNets.end() )
182 return std::nullopt;
183
184 const auto found = sheet->second.find( aItem.m_Uuid );
185 return found == sheet->second.end() ? std::nullopt : std::optional( found->second );
186}
187
188
189// a "less than" test on two LIB_SYMBOLs (.m_name wxStrings)
190bool LIB_SYMBOL_LESS_THAN::operator()( LIB_SYMBOL* const& libsymbol1, LIB_SYMBOL* const& libsymbol2 ) const
191{
192 // Use case specific GetName() wxString compare
193 return libsymbol1->GetLibId() < libsymbol2->GetLibId();
194}
195
196
197wxString NETLIST_EXPORTER_BASE::MakeCommandLine( const wxString& aFormatString, const wxString& aNetlistFile,
198 const wxString& aFinalFile, const wxString& aProjectPath )
199{
200 // Expand format symbols in the command line:
201 // %B => base filename of selected output file, minus path and extension.
202 // %P => project directory name, without trailing '/' or '\'.
203 // %I => full filename of the input file (the intermediate net file).
204 // %O => complete filename and path (but without extension) of the user chosen output file.
205
206 wxString ret = aFormatString;
207 wxFileName in = aNetlistFile;
208 wxFileName out = aFinalFile;
209 wxString str_out = out.GetFullPath();
210
211 ret.Replace( "%P", aProjectPath, true );
212 ret.Replace( "%B", out.GetName(), true );
213 ret.Replace( "%I", in.GetFullPath(), true );
214
215#ifdef __WINDOWS__
216 // A ugly hack to run xsltproc that has a serious bug on Window since a long time:
217 // the filename given after -o option (output filename) cannot use '\' in filename
218 // so replace if by '/' if possible (I mean if the filename does not start by "\\"
219 // that is a filename on a Windows server)
220
221 if( !str_out.StartsWith( "\\\\" ) )
222 str_out.Replace( "\\", "/" );
223#endif
224
225 ret.Replace( "%O", str_out, true );
226
227 return ret;
228}
229
230
232{
233 wxCHECK( aItem, nullptr );
234
235 wxString ref;
236
237 if( aItem->Type() != SCH_SYMBOL_T )
238 return nullptr;
239
240 // found next symbol
241 SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem;
242
243 // Power symbols and other symbols which have the reference starting with "#" are not
244 // included in netlist (pseudo or virtual symbols)
245 ref = symbol->GetRef( &aSheetPath );
246
247 if( ref[0] == wxChar( '#' ) )
248 return nullptr;
249
250 SCH_SCREEN* screen = aSheetPath.LastScreen();
251
252 wxCHECK( screen, nullptr );
253
254 auto it = screen->GetLibSymbols().find( symbol->GetSchSymbolLibraryName() );
255
256 if( it == screen->GetLibSymbols().end() )
257 return nullptr;
258
259 LIB_SYMBOL* libSymbol = it->second;
260
261 // If symbol is a "multi parts per package" type
262 if( libSymbol->GetUnitCount() > 1 )
263 {
264 // test if this reference has already been processed, and if so skip
265 if( m_referencesAlreadyFound.Lookup( ref ) )
266 return nullptr;
267 }
268
269 // record the usage of this library symbol entry.
270 m_libParts.insert( libSymbol ); // rejects non-unique pointers
271
272 return symbol;
273}
274
275
276std::vector<PIN_INFO> NETLIST_EXPORTER_BASE::CreatePinList( SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSheetPath )
277{
278 std::vector<PIN_INFO> pins;
279
280 if( !aSymbol )
281 return pins;
282
283 wxString ref( aSymbol->GetRef( &aSheetPath ) );
284
285 // Power symbols and other symbols which have the reference starting with "#" are not
286 // included in netlist (pseudo or virtual symbols)
287 if( ( ref[0] == wxChar( '#' ) ) || aSymbol->IsPower() )
288 return pins;
289
290 // if( aSymbol->m_FlagControlMulti == 1 )
291 // continue; /* yes */
292 // removed because with multiple instances of one schematic (several sheets pointing to
293 // 1 screen), this will be erroneously be toggled.
294
295 if( !aSymbol->GetLibSymbolRef() )
296 return pins;
297
298 // If symbol is a "multi parts per package" type
299 if( aSymbol->GetLibSymbolRef()->GetUnitCount() > 1 )
300 {
301 // Collect all pins for this reference designator by searching the entire design for
302 // other parts with the same reference designator.
303 findAllUnitsOfSymbol( aSymbol, aSheetPath, pins );
304 }
305
306 else // GetUnitCount() <= 1 means one part per package
307 {
308 for( const SCH_PIN* pin : aSymbol->GetPins( &aSheetPath ) )
309 {
310 if( const std::optional<wxString> netName = itemNetName( *pin, aSheetPath ) )
311 appendResolvedPins( pins, pin, aSheetPath, *netName );
312 }
313 }
314
315 // Sort pins in m_SortedSymbolPinList by pin number
316 std::sort( pins.begin(), pins.end(),
317 []( const PIN_INFO& lhs, const PIN_INFO& rhs )
318 {
319 return StrNumCmp( lhs.num, rhs.num, true ) < 0;
320 } );
321
322 // Remove duplicate Pins in m_SortedSymbolPinList
323 eraseDuplicatePins( pins );
324
325 // record the usage of this library symbol
326 m_libParts.insert( aSymbol->GetLibSymbolRef().get() ); // rejects non-unique pointers
327
328 return pins;
329}
330
331
332std::vector<wxString> NETLIST_EXPORTER_BASE::resolvePadNumbers( const SCH_PIN* aPin,
333 const SCH_SHEET_PATH& aSheetPath ) const
334{
335 // Shared resolution kernel for all netlist paths (issue #2282) so they cannot drift.
336 if( m_kiway )
337 {
338 if( const SCH_SYMBOL* symbol = dynamic_cast<const SCH_SYMBOL*>( aPin->GetParentSymbol() ) )
339 {
340 wxString fpText = symbol->GetFootprintFieldText( &aSheetPath, RESOLVED, m_exportVariant );
341 LIB_ID fpId;
342
343 if( !fpText.IsEmpty() && fpId.Parse( fpText, true ) < 0 )
344 {
345 const std::set<wxString>& pads = footprintPads( fpId.GetUniStringLibId() );
346
347 if( !pads.empty() )
348 {
349 SCH_PIN::PAD_RESOLUTION state = SCH_PIN::PAD_RESOLUTION::MAPPED;
350 wxString pad = aPin->GetEffectivePadNumber( aSheetPath, m_exportVariant, fpId, &pads, &state );
351
352 if( state == SCH_PIN::PAD_RESOLUTION::UNMAPPED )
353 return {};
354
356 }
357 }
358 }
359 }
360
362}
363
364
365const std::set<wxString>& NETLIST_EXPORTER_BASE::footprintPads( const wxString& aFootprintId ) const
366{
367 auto it = m_footprintPadCache.find( aFootprintId );
368
369 if( it != m_footprintPadCache.end() )
370 return it->second;
371
372 std::set<wxString>& pads = m_footprintPadCache[aFootprintId];
373
374 if( m_kiway && !aFootprintId.IsEmpty() )
375 {
376 if( KIFACE* cvpcb = m_kiway->KiFACE( KIWAY::FACE_CVPCB ) )
377 {
378 typedef void ( *PAD_NUMBERS_FN_PTR )( const wxString&, PROJECT*, std::set<wxString>& );
379
380 if( auto fetch = (PAD_NUMBERS_FN_PTR) cvpcb->IfaceOrAddress( KIFACE_FOOTPRINT_PAD_NUMBERS ) )
381 fetch( aFootprintId, &m_schematic->Project(), pads );
382 }
383 }
384
385 return pads;
386}
387
388
389void NETLIST_EXPORTER_BASE::appendResolvedPins( std::vector<PIN_INFO>& aPins, const SCH_PIN* aPin,
390 const SCH_SHEET_PATH& aSheetPath, const wxString& aNetName )
391{
392 const wxString& baseName = aPin->GetShownName();
393
394 for( const wxString& padNum : resolvePadNumbers( aPin, aSheetPath ) )
395 {
396 wxString pinName = baseName.IsEmpty() ? padNum : baseName + wxT( "_" ) + padNum;
397 aPins.emplace_back( padNum, aNetName, pinName, aPin->GetNumber() );
398 }
399}
400
401
402void NETLIST_EXPORTER_BASE::eraseDuplicatePins( std::vector<PIN_INFO>& aPins )
403{
404 for( unsigned ii = 0; ii < aPins.size(); ii++ )
405 {
406 if( aPins[ii].num.empty() )
407 continue;
408
409 // Search for duplicated pins and keep only one. Duplicate pin numbers can occur
410 // for multi-unit symbols with shared pins, or for symbols with the "duplicate pin
411 // numbers are jumpers" flag. In either case, all pins with the same number should
412 // connect to the same net. When they don't (user error), we prefer user-assigned
413 // nets over auto-generated ones to preserve user intent in the netlist.
414 // Because the pin list is sorted by pin number, duplicates are consecutive.
415 unsigned idxBest = ii;
416
417 for( unsigned jj = ii + 1; jj < aPins.size(); jj++ )
418 {
419 if( aPins[jj].num.empty() )
420 continue;
421
422 if( aPins[idxBest].num != aPins[jj].num )
423 break;
424
425 // A genuine many-to-one mapping collision - two *different* symbol pins resolved to
426 // the same pad on *different* nets - must survive so it stays visible and is flagged
427 // by ERC (issue #2282). Shared multi-unit pins and jumpers carry the same source pin
428 // number, so they still collapse below as before.
429 if( aPins[idxBest].netName != aPins[jj].netName
430 && !aPins[idxBest].srcPin.IsEmpty()
431 && !aPins[jj].srcPin.IsEmpty()
432 && aPins[idxBest].srcPin != aPins[jj].srcPin )
433 {
434 continue;
435 }
436
437 // Check if jj has a better (user-assigned) net than the current best.
438 // Prefer user-assigned nets over auto-generated "unconnected-(" or "Net-(" nets.
439 bool bestIsAuto = IsAutoGeneratedNetName( aPins[idxBest].netName );
440 bool jjIsAuto = IsAutoGeneratedNetName( aPins[jj].netName );
441
442 if( bestIsAuto && !jjIsAuto )
443 {
444 // jj has a user-assigned net while best has auto-generated; switch to jj
445 aPins[idxBest].num.clear();
446 idxBest = jj;
447 }
448 else
449 {
450 aPins[jj].num.clear();
451 }
452 }
453 }
454}
455
456
458 std::vector<PIN_INFO>& aPins )
459{
460 const wxString ref = aSchSymbol->GetRef( &aSheetPath );
461
462 for( const SCH_SHEET_PATH& sheet : m_exportSheets )
463 {
464 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
465 {
466 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
467
468 if( symbol->GetRef( &sheet ).CmpNoCase( ref ) != 0 )
469 continue;
470
471 for( const SCH_PIN* pin : symbol->GetPins( &sheet ) )
472 {
473 if( const std::optional<wxString> netName = itemNetName( *pin, sheet ) )
474 appendResolvedPins( aPins, pin, sheet, *netName );
475 }
476 }
477 }
478}
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A subgraph is a set of items that are electrically connected on a single sheet.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
virtual const char * what() const override
std::exception interface, returned as UTF-8
@ FACE_CVPCB
Definition kiway.h:357
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
wxString GetUniStringLibId() const
Definition lib_id.h:144
Define a library symbol object.
Definition lib_symbol.h:114
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
int GetUnitCount() const override
CONNECTIVITY_SCOPE(NETLIST_EXPORTER_BASE &aExporter)
NETLIST_EXPORTER_BASE(SCHEMATIC *aSchematic, KIWAY *aKiway)
virtual bool writeNetlist(const wxString &aOutFileName, unsigned aNetlistOptions, REPORTER &aReporter)
std::optional< wxString > itemNetName(const SCH_ITEM &aItem, const SCH_SHEET_PATH &aPath) const
const std::set< wxString > & footprintPads(const wxString &aFootprintId) const
void findAllUnitsOfSymbol(SCH_SYMBOL *aSchSymbol, const SCH_SHEET_PATH &aSheetPath, std::vector< PIN_INFO > &aPins)
Find all units for symbols with multiple symbols per package.
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, const SCH_SHEET_PATH &aSheetPath)
Check if the given symbol should be processed for netlisting.
std::map< wxString, std::set< wxString > > m_footprintPadCache
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
void eraseDuplicatePins(std::vector< PIN_INFO > &pins)
Erase duplicate pins.
std::vector< wxString > resolvePadNumbers(const SCH_PIN *aPin, const SCH_SHEET_PATH &aSheetPath) const
Resolve aPin to its effective pad number(s) for every netlist path (issue #2282).
std::map< SCH_SHEET_PATH, std::unordered_map< KIID, wxString > > m_exportItemNets
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
std::vector< EXPORT_NET > m_exportNets
bool WriteNetlist(const wxString &aOutFileName, unsigned aNetlistOptions, REPORTER &aReporter)
Write to specified output file.
wxString m_exportVariant
The schematic we're generating a netlist for.
KIWAY * m_kiway
The variant of the schematic to export.
static wxString MakeCommandLine(const wxString &aFormatString, const wxString &aNetlistFile, const wxString &aFinalFile, const wxString &aProjectDirectory)
Build up a string that describes a command line for executing a child process.
void appendResolvedPins(std::vector< PIN_INFO > &aPins, const SCH_PIN *aPin, const SCH_SHEET_PATH &aSheetPath, const wxString &aNetName)
Resolve aPin to its pad number(s) and append a PIN_INFO for each (issue #2282).
std::vector< PIN_INFO > CreatePinList(SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSheetPath)
Find a symbol from the DrawList and builds its pin list.
Container for project specific data.
Definition project.h:63
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
std::vector< SCH_ITEM * > Items() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:350
PAD_RESOLUTION
Outcome of pin-to-pad resolution (issue #2282).
Definition sch_pin.h:175
const wxString & GetShownName() const
Definition sch_pin.cpp:697
wxString GetEffectivePadNumber(const SCH_SHEET_PATH &aSheet, const wxString &aVariantName, const LIB_ID &aFootprintLibId, const std::set< wxString > *aFootprintPadNumbers, PAD_RESOLUTION *aState=nullptr) const
Resolve the footprint pad number this symbol pin lands on (issue #2282).
Definition sch_pin.cpp:748
const wxString & GetNumber() const
Definition sch_pin.h:144
const std::map< wxString, LIB_SYMBOL * > & GetLibSymbols() const
Fetch a list of unique LIB_SYMBOL object pointers required to properly render each SCH_SYMBOL in this...
Definition sch_screen.h:557
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SCREEN * LastScreen()
Schematic symbol object.
Definition sch_symbol.h:73
wxString GetSchSymbolLibraryName() const
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
bool IsPower() const override
@ RESOLVED
Definition common.h:95
#define _(s)
@ NO_RECURSE
Definition eda_item.h:52
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
@ KIFACE_FOOTPRINT_PAD_NUMBERS
Function pointer type: void (*)( const wxString& aFootprint, PROJECT* aProject, std::set<wxString>& a...
Definition kiface_ids.h:87
@ RPT_SEVERITY_ERROR
std::vector< wxString > ExpandStackedPinNotation(const wxString &aPinName, bool *aValid)
Expand stacked pin notation like [1,2,3], [1-4], [A1-A4], or [AA1-AA3,AB4,CD12-CD14] into individual ...
bool IsAutoGeneratedNetName(const wxString &aNetName)
Recognize the reserved prefixes used for generated pin-net names.
Implement a participant in the KIWAY alchemy.
Definition kiway.h:153
bool operator()(LIB_SYMBOL *const &libsymbol1, LIB_SYMBOL *const &libsymbol2) const
std::string path
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
@ SCH_NO_CONNECT_T
Definition typeinfo.h:156
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_PIN_T
Definition typeinfo.h:149