KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_schematic_parity.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 The KiCad Developers.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <board.h>
21#include <footprint.h>
22#include <pad.h>
23#include <drc/drc_engine.h>
24#include <drc/drc_item.h>
25#include <drc/drc_rule.h>
27
28#include <kiway.h>
30
31/*
32 Schematic parity test.
33
34 Errors generated:
35 - DRCE_MISSING_FOOTPRINT
36 - DRCE_DUPLICATE_FOOTPRINT
37 - DRCE_EXTRA_FOOTPRINT
38 - DRCE_SCHEMATIC_PARITY
39 - DRCE_FOOTPRINT_FILTERS
40
41 TODO:
42 - cross-check PCB netlist against SCH netlist
43 - cross-check PCB fields against SCH fields
44*/
45
47{
48public:
53
55
56 virtual bool Run() override;
57
58 virtual const wxString GetName() const override { return wxT( "schematic_parity" ); };
59
60private:
61 void testNetlist( NETLIST& aNetlist );
62};
63
64
66{
67 BOARD* board = m_drcEngine->GetBoard();
68
69 auto compare = []( const FOOTPRINT* x, const FOOTPRINT* y )
70 {
71 return x->GetReference().CmpNoCase( y->GetReference() ) < 0;
72 };
73
74 auto footprints = std::set<FOOTPRINT*, decltype( compare )>( compare );
75
76 // Search for duplicate footprints on the board
77 for( FOOTPRINT* footprint : board->Footprints() )
78 {
79 if( m_drcEngine->IsErrorLimitExceeded( DRCE_DUPLICATE_FOOTPRINT ) )
80 break;
81
82 auto ins = footprints.insert( footprint );
83
84 if( !ins.second && !( footprint->GetAttributes() & FP_BOARD_ONLY ) )
85 {
86 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DUPLICATE_FOOTPRINT );
87 drcItem->SetItems( footprint, *ins.first );
88
89 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
90 }
91 }
92
93 // Search for component footprints in the netlist but not on the board.
94 for( unsigned ii = 0; ii < aNetlist.GetCount(); ii++ )
95 {
96 COMPONENT* component = aNetlist.GetComponent( ii );
97 FOOTPRINT* footprint = board->FindFootprintByReference( component->GetReference() );
98
99 if( footprint == nullptr )
100 {
101 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_FOOTPRINT ) )
102 {
103 wxString msg;
104 msg.Printf( _( "Missing footprint %s (%s)" ),
105 component->GetReference(),
106 component->GetValue() );
107
108 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_FOOTPRINT );
109
110 drcItem->SetErrorMessage( msg );
112 }
113 }
114 else
115 {
116 if( component->GetValue() != footprint->GetValue()
117 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
118 {
119 wxString msg;
120 msg.Printf( _( "Value (%s) doesn't match symbol value (%s)" ),
121 footprint->GetReference(), footprint->GetValue(),
122 component->GetValue() );
123
124 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
125 drcItem->SetErrorMessage( msg );
126 drcItem->SetItems( footprint );
127 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
128 }
129
130 if( component->GetFPID().GetUniStringLibId() != footprint->GetFPID().GetUniStringLibId()
131 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
132 {
133 wxString msg;
134 msg.Printf( _( "%s doesn't match footprint given by symbol (%s)" ),
135 footprint->GetFPID().GetUniStringLibId(),
136 component->GetFPID().GetUniStringLibId() );
137
138 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
139 drcItem->SetErrorMessage( msg );
140 drcItem->SetItems( footprint );
141 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
142 }
143
144 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_FOOTPRINT_FILTERS ) )
145 {
146 wxString libIdLower = footprint->GetFPID().GetUniStringLibId().Lower();
147 wxString fpNameLower = footprint->GetFPID().GetUniStringLibItemName().Lower();
148 size_t filtercount = component->GetFootprintFilters().GetCount();
149 bool found = ( 0 == filtercount ); // if no entries, do not filter
150
151 for( size_t jj = 0; jj < filtercount && !found; jj++ )
152 {
153 wxString filterLower = component->GetFootprintFilters()[jj].Lower();
154
155 if( filterLower.Find( ':' ) == wxNOT_FOUND )
156 found = fpNameLower.Matches( filterLower );
157 else
158 found = libIdLower.Matches( filterLower );
159 }
160
161 if( !found )
162 {
163 wxString msg;
164 msg.Printf( _( "%s doesn't match symbol's footprint filters (%s)" ),
165 footprint->GetFPID().GetUniStringLibId(),
166 wxJoin( component->GetFootprintFilters(), ' ' ) );
167
168 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_FOOTPRINT_FILTERS );
169 drcItem->SetErrorMessage( msg );
170 drcItem->SetItems( footprint );
171 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
172 }
173 }
174
175 if( ( component->GetProperties().count( "dnp" ) > 0 )
176 != ( ( footprint->GetAttributes() & FP_DNP ) > 0 )
177 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
178 {
179 wxString msg;
180 msg.Printf( _( "'%s' settings differ" ), _( "Do not populate" ) );
181
182 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
183 drcItem->SetErrorMessage( drcItem->GetErrorMessage( true ) + wxS( ": " ) + msg );
184 drcItem->SetItems( footprint );
185 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
186 }
187
188 if( ( component->GetProperties().count( "exclude_from_bom" ) > 0 )
189 != ( (footprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 )
190 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
191 {
192 wxString msg;
193 msg.Printf( _( "'%s' settings differ" ), _( "Exclude from bill of materials" ) );
194
195 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
196 drcItem->SetErrorMessage( drcItem->GetErrorMessage( true ) + wxS( ": " ) + msg );
197 drcItem->SetItems( footprint );
198 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
199 }
200
201 if( ( component->GetProperties().count( "exclude_from_sim" ) > 0 )
202 != ( ( footprint->GetAttributes() & FP_EXCLUDE_FROM_SIM ) > 0 )
203 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
204 {
205 wxString msg;
206 msg.Printf( _( "'%s' settings differ" ), _( "Exclude from simulation" ) );
207
208 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
209 drcItem->SetErrorMessage( drcItem->GetErrorMessage( true ) + wxS( ": " ) + msg );
210 drcItem->SetItems( footprint );
211 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
212 }
213
214 // Compare custom fields between schematic component and PCB footprint
215 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_FIELDS_PARITY ) )
216 {
217 std::unordered_map<wxString, wxString> fpFieldsAsMap;
218
219 for( PCB_FIELD* field : footprint->GetFields() )
220 {
221 wxCHECK2( field, continue );
222
223 if( field->IsReference() || field->IsValue() || field->IsComponentClass() )
224 continue;
225
226 fpFieldsAsMap[field->GetName()] = field->GetText();
227 }
228
229 // Remove the extra component fields we don't want to evaluate here
230 nlohmann::ordered_map<wxString, wxString> compFields = component->GetFields();
232 compFields.erase( GetDefaultFieldName( FIELD_T::VALUE, UNTRANSLATED ) );
234 compFields.erase( wxT( "Component Class" ) );
235
236 bool fieldsMatch = true;
237 wxString mismatchDetail;
238
239 for( const auto& [name, value] : compFields )
240 {
241 auto it = fpFieldsAsMap.find( name );
242
243 if( it == fpFieldsAsMap.end() )
244 {
245 fieldsMatch = false;
246 mismatchDetail = wxString::Format( _( "Missing symbol field '%s' in footprint" ), name );
247 break;
248 }
249
250 if( it->second != value )
251 {
252 fieldsMatch = false;
253 mismatchDetail = wxString::Format( _( "Field '%s' differs (PCB: '%s', Schematic: '%s')" ),
254 name, it->second, value );
255 break;
256 }
257 }
258
259 if( !fieldsMatch && !mismatchDetail.IsEmpty() )
260 {
261 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_FIELDS_PARITY );
262
263 drcItem->SetErrorMessage( mismatchDetail );
264 drcItem->SetItems( footprint );
265 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
266 }
267 }
268
269 for( PAD* pad : footprint->Pads() )
270 {
271 if( m_drcEngine->IsErrorLimitExceeded( DRCE_NET_CONFLICT ) )
272 break;
273
274 if( !pad->CanHaveNumber() )
275 continue;
276
277 const COMPONENT_NET& sch_net = component->GetNet( pad->GetNumber() );
278 const wxString& pcb_netname = pad->GetNetname();
279
280 if( !pcb_netname.IsEmpty() && sch_net.GetPinName().IsEmpty() )
281 {
282 wxString msg;
283 msg.Printf( _( "No corresponding pin found in schematic" ) );
284
285 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
286 drcItem->SetErrorMessage( msg );
287 drcItem->SetItems( pad );
288 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
289 }
290 else if( pcb_netname.IsEmpty() && !sch_net.GetNetName().IsEmpty() )
291 {
292 wxString msg;
293 msg.Printf( _( "Pad missing net given by schematic (%s)" ),
294 sch_net.GetNetName() );
295
296 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
297 drcItem->SetErrorMessage( msg );
298 drcItem->SetItems( pad );
299 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
300 }
301 else if( pcb_netname != sch_net.GetNetName()
302 && !( pcb_netname.starts_with(
303 wxT( "unconnected-" ) )
304 && pcb_netname.starts_with( sch_net.GetNetName() ) )
305 && !( pad->IsNoConnectPad()
306 && pcb_netname.starts_with( sch_net.GetNetName() + wxT( "_" ) ) ))
307 {
308 wxString msg;
309 msg.Printf( _( "Pad net (%s) doesn't match net given by schematic (%s)" ),
310 pcb_netname,
311 sch_net.GetNetName() );
312
313 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
314 drcItem->SetErrorMessage( msg );
315 drcItem->SetItems( pad );
316 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
317 }
318 }
319
320 for( unsigned jj = 0; jj < component->GetNetCount(); ++jj )
321 {
322 if( m_drcEngine->IsErrorLimitExceeded( DRCE_NET_CONFLICT ) )
323 break;
324
325 const COMPONENT_NET& sch_net = component->GetNet( jj );
326
327 if( !footprint->FindPadByNumber( sch_net.GetPinName() ) )
328 {
329 wxString msg;
330
331 if( sch_net.GetNetName().StartsWith( wxT( "unconnected-" ) ) )
332 {
333 msg = sch_net.GetPinName();
334 }
335 else
336 {
337 msg = wxString::Format( wxT( "%s (%s)" ),
338 sch_net.GetPinName(),
339 sch_net.GetNetName() );
340 }
341
342 msg.Printf( _( "No pad found for pin %s in schematic" ), msg );
343
344 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
345 drcItem->SetErrorMessage( msg );
346 drcItem->SetItems( footprint );
347 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
348 }
349 }
350 }
351 }
352
353 // Search for component footprints found on board but not in netlist.
354 for( FOOTPRINT* footprint : board->Footprints() )
355 {
356 if( m_drcEngine->IsErrorLimitExceeded( DRCE_EXTRA_FOOTPRINT ) )
357 break;
358
359 if( footprint->GetAttributes() & FP_BOARD_ONLY )
360 continue;
361
362 if( !aNetlist.GetComponentByReference( footprint->GetReference() ) )
363 {
364 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_EXTRA_FOOTPRINT );
365
366 drcItem->SetItems( footprint );
367 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
368 }
369 }
370}
371
372
374{
375 if( m_drcEngine->GetTestFootprints() )
376 {
377 if( !reportPhase( _( "Checking PCB to schematic parity..." ) ) )
378 return false;
379
380 auto netlist = m_drcEngine->GetSchematicNetlist();
381
382 if( !netlist )
383 {
384 REPORT_AUX( wxT( "No netlist provided, skipping schematic parity tests." ) );
385 return true;
386 }
387
389 }
390
391 return !m_drcEngine->IsCancelled();
392}
393
394
395namespace detail
396{
398}
const char * name
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
const FOOTPRINTS & Footprints() const
Definition board.h:463
FOOTPRINT * FindFootprintByReference(const wxString &aReference) const
Search for a FOOTPRINT within this board with the given reference designator.
Definition board.cpp:3062
Used to store the component pin name to net name (and pin function) associations stored in a netlist.
const wxString & GetNetName() const
const wxString & GetPinName() const
Store all of the related component information found in a netlist.
const COMPONENT_NET & GetNet(unsigned aIndex) const
const wxString & GetReference() const
const wxString & GetValue() const
const nlohmann::ordered_map< wxString, wxString > & GetFields() const
const std::map< wxString, wxString > & GetProperties() const
const wxArrayString & GetFootprintFilters() const
const LIB_ID & GetFPID() const
unsigned GetNetCount() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:444
virtual const wxString GetName() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual ~DRC_TEST_PROVIDER_SCHEMATIC_PARITY()=default
virtual bool reportPhase(const wxString &aStageName)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
std::deque< PAD * > & Pads()
Definition footprint.h:404
int GetAttributes() const
Definition footprint.h:550
const LIB_ID & GetFPID() const
Definition footprint.h:473
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
const wxString & GetValue() const
Definition footprint.h:925
const wxString & GetReference() const
Definition footprint.h:901
VECTOR2I GetPosition() const override
Definition footprint.h:435
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
wxString GetUniStringLibId() const
Definition lib_id.h:144
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
unsigned GetCount() const
COMPONENT * GetComponentByReference(const wxString &aReference)
Return a COMPONENT by aReference.
COMPONENT * GetComponent(unsigned aIndex)
Return the COMPONENT at aIndex.
Definition pad.h:61
@ DRCE_FOOTPRINT_FILTERS
Definition drc_item.h:82
@ DRCE_SCHEMATIC_FIELDS_PARITY
Definition drc_item.h:126
@ DRCE_DUPLICATE_FOOTPRINT
Definition drc_item.h:78
@ DRCE_EXTRA_FOOTPRINT
Definition drc_item.h:79
@ DRCE_NET_CONFLICT
Definition drc_item.h:80
@ DRCE_MISSING_FOOTPRINT
Definition drc_item.h:77
@ DRCE_SCHEMATIC_PARITY
Definition drc_item.h:81
#define REPORT_AUX(s)
#define _(s)
@ FP_DNP
Definition footprint.h:91
@ FP_BOARD_ONLY
Definition footprint.h:89
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:88
@ FP_EXCLUDE_FROM_SIM
Definition footprint.h:92
@ UNDEFINED_LAYER
Definition layer_ids.h:57
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
@ UNTRANSLATED
std::string netlist
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683