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
37
38// a "less than" test on two LIB_SYMBOLs (.m_name wxStrings)
40 LIB_SYMBOL* const& libsymbol2 ) const
41{
42 // Use case specific GetName() wxString compare
43 return libsymbol1->GetLibId() < libsymbol2->GetLibId();
44}
45
46
47wxString NETLIST_EXPORTER_BASE::MakeCommandLine( const wxString& aFormatString,
48 const wxString& aNetlistFile,
49 const wxString& aFinalFile,
50 const wxString& aProjectPath )
51{
52 // Expand format symbols in the command line:
53 // %B => base filename of selected output file, minus path and extension.
54 // %P => project directory name, without trailing '/' or '\'.
55 // %I => full filename of the input file (the intermediate net file).
56 // %O => complete filename and path (but without extension) of the user chosen output file.
57
58 wxString ret = aFormatString;
59 wxFileName in = aNetlistFile;
60 wxFileName out = aFinalFile;
61 wxString str_out = out.GetFullPath();
62
63 ret.Replace( "%P", aProjectPath, true );
64 ret.Replace( "%B", out.GetName(), true );
65 ret.Replace( "%I", in.GetFullPath(), true );
66
67#ifdef __WINDOWS__
68 // A ugly hack to run xsltproc that has a serious bug on Window since a long time:
69 // the filename given after -o option (output filename) cannot use '\' in filename
70 // so replace if by '/' if possible (I mean if the filename does not start by "\\"
71 // that is a filename on a Windows server)
72
73 if( !str_out.StartsWith( "\\\\" ) )
74 str_out.Replace( "\\", "/" );
75#endif
76
77 ret.Replace( "%O", str_out, true );
78
79 return ret;
80}
81
82
84 const SCH_SHEET_PATH& aSheetPath )
85{
86 wxCHECK( aItem, nullptr );
87
88 wxString ref;
89
90 if( aItem->Type() != SCH_SYMBOL_T )
91 return nullptr;
92
93 // found next symbol
94 SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem;
95
96 // Power symbols and other symbols which have the reference starting with "#" are not
97 // included in netlist (pseudo or virtual symbols)
98 ref = symbol->GetRef( &aSheetPath );
99
100 if( ref[0] == wxChar( '#' ) )
101 return nullptr;
102
103 SCH_SCREEN* screen = aSheetPath.LastScreen();
104
105 wxCHECK( screen, nullptr );
106
107 auto it = screen->GetLibSymbols().find( symbol->GetSchSymbolLibraryName() );
108
109 if( it == screen->GetLibSymbols().end() )
110 return nullptr;
111
112 LIB_SYMBOL* libSymbol = it->second;
113
114 // If symbol is a "multi parts per package" type
115 if( libSymbol->GetUnitCount() > 1 )
116 {
117 // test if this reference has already been processed, and if so skip
118 if( m_referencesAlreadyFound.Lookup( ref ) )
119 return nullptr;
120 }
121
122 // record the usage of this library symbol entry.
123 m_libParts.insert( libSymbol ); // rejects non-unique pointers
124
125 return symbol;
126}
127
128
129std::vector<PIN_INFO> NETLIST_EXPORTER_BASE::CreatePinList( SCH_SYMBOL* aSymbol,
130 const SCH_SHEET_PATH& aSheetPath,
131 bool aKeepUnconnectedPins )
132{
133 std::vector<PIN_INFO> pins;
134
135 if( !aSymbol )
136 return pins;
137
138 wxString ref( aSymbol->GetRef( &aSheetPath ) );
139
140 // Power symbols and other symbols which have the reference starting with "#" are not
141 // included in netlist (pseudo or virtual symbols)
142 if( ( ref[0] == wxChar( '#' ) ) || aSymbol->IsPower() )
143 return pins;
144
145 // if( aSymbol->m_FlagControlMulti == 1 )
146 // continue; /* yes */
147 // removed because with multiple instances of one schematic (several sheets pointing to
148 // 1 screen), this will be erroneously be toggled.
149
150 if( !aSymbol->GetLibSymbolRef() )
151 return pins;
152
153 // If symbol is a "multi parts per package" type
154 if( aSymbol->GetLibSymbolRef()->GetUnitCount() > 1 )
155 {
156 // Collect all pins for this reference designator by searching the entire design for
157 // other parts with the same reference designator.
158 findAllUnitsOfSymbol( aSymbol, aSheetPath, pins, aKeepUnconnectedPins );
159 }
160
161 else // GetUnitCount() <= 1 means one part per package
162 {
163 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
164
165 for( const SCH_PIN* pin : aSymbol->GetPins( &aSheetPath ) )
166 {
167 if( SCH_CONNECTION* conn = pin->Connection( &aSheetPath ) )
168 {
169 const wxString& netName = conn->Name();
170
171 if( !aKeepUnconnectedPins ) // Skip unconnected pins if requested
172 {
173 CONNECTION_SUBGRAPH* sg = graph->FindSubgraphByName( netName, aSheetPath );
174
175 if( !sg || sg->GetNoConnect() || sg->GetItems().size() < 2 )
176 continue;
177 }
178
179 appendResolvedPins( pins, pin, aSheetPath, netName );
180 }
181 }
182 }
183
184 // Sort pins in m_SortedSymbolPinList by pin number
185 std::sort( pins.begin(), pins.end(),
186 []( const PIN_INFO& lhs, const PIN_INFO& rhs )
187 {
188 return StrNumCmp( lhs.num, rhs.num, true ) < 0;
189 } );
190
191 // Remove duplicate Pins in m_SortedSymbolPinList
192 eraseDuplicatePins( pins );
193
194 // record the usage of this library symbol
195 m_libParts.insert( aSymbol->GetLibSymbolRef().get() ); // rejects non-unique pointers
196
197 return pins;
198}
199
200
201std::vector<wxString> NETLIST_EXPORTER_BASE::resolvePadNumbers( const SCH_PIN* aPin,
202 const SCH_SHEET_PATH& aSheetPath ) const
203{
204 // Shared resolution kernel for all netlist paths (issue #2282) so they cannot drift.
205 const wxString variantName = m_schematic ? m_schematic->GetCurrentVariant() : wxString();
206
207 if( m_kiway )
208 {
209 if( const SCH_SYMBOL* symbol = dynamic_cast<const SCH_SYMBOL*>( aPin->GetParentSymbol() ) )
210 {
211 wxString fpText = symbol->GetFootprintFieldText( true, &aSheetPath, false, variantName );
212 LIB_ID fpId;
213
214 if( !fpText.IsEmpty() && fpId.Parse( fpText, true ) < 0 )
215 {
216 const std::set<wxString>& pads = footprintPads( fpId.GetUniStringLibId() );
217
218 if( !pads.empty() )
219 {
220 SCH_PIN::PAD_RESOLUTION state = SCH_PIN::PAD_RESOLUTION::MAPPED;
221 wxString pad = aPin->GetEffectivePadNumber( aSheetPath, variantName, fpId, &pads, &state );
222
223 if( state == SCH_PIN::PAD_RESOLUTION::UNMAPPED )
224 return {};
225
227 }
228 }
229 }
230 }
231
232 return ExpandStackedPinNotation( aPin->GetEffectivePadNumber( aSheetPath, variantName ) );
233}
234
235
236const std::set<wxString>& NETLIST_EXPORTER_BASE::footprintPads( const wxString& aFootprintId ) const
237{
238 auto it = m_footprintPadCache.find( aFootprintId );
239
240 if( it != m_footprintPadCache.end() )
241 return it->second;
242
243 std::set<wxString>& pads = m_footprintPadCache[aFootprintId];
244
245 if( m_kiway && !aFootprintId.IsEmpty() )
246 {
247 if( KIFACE* cvpcb = m_kiway->KiFACE( KIWAY::FACE_CVPCB ) )
248 {
249 typedef void ( *PAD_NUMBERS_FN_PTR )( const wxString&, PROJECT*, std::set<wxString>& );
250
251 if( auto fetch = (PAD_NUMBERS_FN_PTR) cvpcb->IfaceOrAddress( KIFACE_FOOTPRINT_PAD_NUMBERS ) )
252 fetch( aFootprintId, &m_schematic->Project(), pads );
253 }
254 }
255
256 return pads;
257}
258
259
260void NETLIST_EXPORTER_BASE::appendResolvedPins( std::vector<PIN_INFO>& aPins, const SCH_PIN* aPin,
261 const SCH_SHEET_PATH& aSheetPath, const wxString& aNetName )
262{
263 const wxString baseName = aPin->GetShownName();
264
265 for( const wxString& padNum : resolvePadNumbers( aPin, aSheetPath ) )
266 {
267 wxString pinName = baseName.IsEmpty() ? padNum : baseName + wxT( "_" ) + padNum;
268 aPins.emplace_back( padNum, aNetName, pinName, aPin->GetNumber() );
269 }
270}
271
272
273void NETLIST_EXPORTER_BASE::eraseDuplicatePins( std::vector<PIN_INFO>& aPins )
274{
275 // Helper to check if a net name is auto-generated rather than user-assigned.
276 // Auto-generated nets start with "unconnected-(" for NC pins or "Net-(" for unnamed nets.
277 auto isAutoGeneratedNet = []( const wxString& aNetName ) -> bool
278 {
279 return aNetName.StartsWith( wxT( "unconnected-(" ) )
280 || aNetName.StartsWith( wxT( "Net-(" ) );
281 };
282
283 for( unsigned ii = 0; ii < aPins.size(); ii++ )
284 {
285 if( aPins[ii].num.empty() )
286 continue;
287
288 // Search for duplicated pins and keep only one. Duplicate pin numbers can occur
289 // for multi-unit symbols with shared pins, or for symbols with the "duplicate pin
290 // numbers are jumpers" flag. In either case, all pins with the same number should
291 // connect to the same net. When they don't (user error), we prefer user-assigned
292 // nets over auto-generated ones to preserve user intent in the netlist.
293 // Because the pin list is sorted by pin number, duplicates are consecutive.
294 unsigned idxBest = ii;
295
296 for( unsigned jj = ii + 1; jj < aPins.size(); jj++ )
297 {
298 if( aPins[jj].num.empty() )
299 continue;
300
301 if( aPins[idxBest].num != aPins[jj].num )
302 break;
303
304 // A genuine many-to-one mapping collision - two *different* symbol pins resolved to
305 // the same pad on *different* nets - must survive so it stays visible and is flagged
306 // by ERC (issue #2282). Shared multi-unit pins and jumpers carry the same source pin
307 // number, so they still collapse below as before.
308 if( aPins[idxBest].netName != aPins[jj].netName && !aPins[idxBest].srcPin.IsEmpty()
309 && !aPins[jj].srcPin.IsEmpty() && aPins[idxBest].srcPin != aPins[jj].srcPin )
310 {
311 continue;
312 }
313
314 // Check if jj has a better (user-assigned) net than the current best.
315 // Prefer user-assigned nets over auto-generated "unconnected-(" or "Net-(" nets.
316 bool bestIsAuto = isAutoGeneratedNet( aPins[idxBest].netName );
317 bool jjIsAuto = isAutoGeneratedNet( aPins[jj].netName );
318
319 if( bestIsAuto && !jjIsAuto )
320 {
321 // jj has a user-assigned net while best has auto-generated; switch to jj
322 aPins[idxBest].num.clear();
323 idxBest = jj;
324 }
325 else
326 {
327 aPins[jj].num.clear();
328 }
329 }
330 }
331}
332
333
335 const SCH_SHEET_PATH& aSheetPath,
336 std::vector<PIN_INFO>& aPins,
337 bool aKeepUnconnectedPins )
338{
339 wxString ref = aSchSymbol->GetRef( &aSheetPath );
340 wxString ref2;
341
342 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
343
344 for( const SCH_SHEET_PATH& sheet : m_schematic->Hierarchy() )
345 {
346 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
347 {
348 SCH_SYMBOL* comp2 = static_cast<SCH_SYMBOL*>( item );
349
350 ref2 = comp2->GetRef( &sheet );
351
352 if( ref2.CmpNoCase( ref ) != 0 )
353 continue;
354
355 for( const SCH_PIN* pin : comp2->GetPins( &sheet ) )
356 {
357 if( SCH_CONNECTION* conn = pin->Connection( &sheet ) )
358 {
359 const wxString& netName = conn->Name();
360
361 if( !aKeepUnconnectedPins ) // Skip unconnected pins if requested
362 {
363 CONNECTION_SUBGRAPH* sg = graph->FindSubgraphByName( netName, sheet );
364
365 if( !sg || sg->GetNoConnect() || sg->GetItems().size() < 2 )
366 continue;
367 }
368
369 appendResolvedPins( aPins, pin, sheet, netName );
370 }
371 }
372 }
373 }
374}
Calculate the connectivity of a schematic and generates netlists.
CONNECTION_SUBGRAPH * FindSubgraphByName(const wxString &aNetName, const SCH_SHEET_PATH &aPath)
Return the subgraph for a given net name on a given sheet.
A subgraph is a set of items that are electrically connected on a single sheet.
const std::set< SCH_ITEM * > & GetItems() const
Provide a read-only reference to the items in the subgraph.
const SCH_ITEM * GetNoConnect() const
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
@ FACE_CVPCB
Definition kiway.h:320
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:80
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:149
int GetUnitCount() const override
SCHEMATIC * m_schematic
The schematic we're generating a netlist for.
std::vector< PIN_INFO > CreatePinList(SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSheetPath, bool aKeepUnconnectedPins)
Find a symbol from the DrawList and builds its pin list.
const std::set< wxString > & footprintPads(const wxString &aFootprintId) const
void findAllUnitsOfSymbol(SCH_SYMBOL *aSchSymbol, const SCH_SHEET_PATH &aSheetPath, std::vector< PIN_INFO > &aPins, bool aKeepUnconnectedPins)
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).
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
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).
Container for project specific data.
Definition project.h:62
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:274
PAD_RESOLUTION
Outcome of pin-to-pad resolution (issue #2282).
Definition sch_pin.h:161
const wxString & GetShownName() const
Definition sch_pin.cpp:676
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:727
const wxString & GetNumber() const
Definition sch_pin.h:130
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:494
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:69
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:177
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
bool IsPower() const override
@ KIFACE_FOOTPRINT_PAD_NUMBERS
Function pointer type: void (*)( const wxString& aFootprint, PROJECT* aProject, std::set<wxString>& a...
Definition kiface_ids.h:62
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 ...
Implement a participant in the KIWAY alchemy.
Definition kiway.h:152
bool operator()(LIB_SYMBOL *const &libsymbol1, LIB_SYMBOL *const &libsymbol2) const
KIBIS_PIN * pin
wxLogTrace helper definitions.
@ SCH_SYMBOL_T
Definition typeinfo.h:169