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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <board.h>
25#include <footprint.h>
26#include <pad.h>
27#include <drc/drc_engine.h>
28#include <drc/drc_item.h>
29#include <drc/drc_rule.h>
31
32#include <kiway.h>
34
35/*
36 Schematic parity test.
37
38 Errors generated:
39 - DRCE_MISSING_FOOTPRINT
40 - DRCE_DUPLICATE_FOOTPRINT
41 - DRCE_EXTRA_FOOTPRINT
42 - DRCE_SCHEMATIC_PARITY
43 - DRCE_FOOTPRINT_FILTERS
44
45 TODO:
46 - cross-check PCB netlist against SCH netlist
47 - cross-check PCB fields against SCH fields
48*/
49
51{
52public:
57
59
60 virtual bool Run() override;
61
62 virtual const wxString GetName() const override { return wxT( "schematic_parity" ); };
63
64private:
65 void testNetlist( NETLIST& aNetlist );
66};
67
68
70{
71 BOARD* board = m_drcEngine->GetBoard();
72
73 auto compare = []( const FOOTPRINT* x, const FOOTPRINT* y )
74 {
75 return x->GetReference().CmpNoCase( y->GetReference() ) < 0;
76 };
77
78 auto footprints = std::set<FOOTPRINT*, decltype( compare )>( compare );
79
80 // Search for duplicate footprints on the board
81 for( FOOTPRINT* footprint : board->Footprints() )
82 {
83 if( m_drcEngine->IsErrorLimitExceeded( DRCE_DUPLICATE_FOOTPRINT ) )
84 break;
85
86 auto ins = footprints.insert( footprint );
87
88 if( !ins.second && !( footprint->GetAttributes() & FP_BOARD_ONLY ) )
89 {
90 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DUPLICATE_FOOTPRINT );
91 drcItem->SetItems( footprint, *ins.first );
92
93 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
94 }
95 }
96
97 // Search for component footprints in the netlist but not on the board.
98 for( unsigned ii = 0; ii < aNetlist.GetCount(); ii++ )
99 {
100 COMPONENT* component = aNetlist.GetComponent( ii );
101 FOOTPRINT* footprint = board->FindFootprintByReference( component->GetReference() );
102
103 if( footprint == nullptr )
104 {
105 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_FOOTPRINT ) )
106 {
107 wxString msg;
108 msg.Printf( _( "Missing footprint %s (%s)" ),
109 component->GetReference(),
110 component->GetValue() );
111
112 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_FOOTPRINT );
113
114 drcItem->SetErrorMessage( msg );
116 }
117 }
118 else
119 {
120 if( component->GetValue() != footprint->GetValue()
121 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
122 {
123 wxString msg;
124 msg.Printf( _( "Value (%s) doesn't match symbol value (%s)" ),
125 footprint->GetReference(), footprint->GetValue(),
126 component->GetValue() );
127
128 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
129 drcItem->SetErrorMessage( msg );
130 drcItem->SetItems( footprint );
131 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
132 }
133
134 if( component->GetFPID().GetUniStringLibId() != footprint->GetFPID().GetUniStringLibId()
135 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
136 {
137 wxString msg;
138 msg.Printf( _( "%s doesn't match footprint given by symbol (%s)" ),
139 footprint->GetFPID().GetUniStringLibId(),
140 component->GetFPID().GetUniStringLibId() );
141
142 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
143 drcItem->SetErrorMessage( msg );
144 drcItem->SetItems( footprint );
145 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
146 }
147
148 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_FOOTPRINT_FILTERS ) )
149 {
150 wxString libIdLower = footprint->GetFPID().GetUniStringLibId().Lower();
151 wxString fpNameLower = footprint->GetFPID().GetUniStringLibItemName().Lower();
152 size_t filtercount = component->GetFootprintFilters().GetCount();
153 bool found = ( 0 == filtercount ); // if no entries, do not filter
154
155 for( size_t jj = 0; jj < filtercount && !found; jj++ )
156 {
157 wxString filterLower = component->GetFootprintFilters()[jj].Lower();
158
159 if( filterLower.Find( ':' ) == wxNOT_FOUND )
160 found = fpNameLower.Matches( filterLower );
161 else
162 found = libIdLower.Matches( filterLower );
163 }
164
165 if( !found )
166 {
167 wxString msg;
168 msg.Printf( _( "%s doesn't match symbol's footprint filters (%s)" ),
169 footprint->GetFPID().GetUniStringLibId(),
170 wxJoin( component->GetFootprintFilters(), ' ' ) );
171
172 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_FOOTPRINT_FILTERS );
173 drcItem->SetErrorMessage( msg );
174 drcItem->SetItems( footprint );
175 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
176 }
177 }
178
179 if( ( component->GetProperties().count( "dnp" ) > 0 )
180 != ( ( footprint->GetAttributes() & FP_DNP ) > 0 )
181 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
182 {
183 wxString msg;
184 msg.Printf( _( "'%s' settings differ" ), _( "Do not populate" ) );
185
186 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
187 drcItem->SetErrorMessage( drcItem->GetErrorMessage( true ) + wxS( ": " ) + msg );
188 drcItem->SetItems( footprint );
189 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
190 }
191
192 if( ( component->GetProperties().count( "exclude_from_bom" ) > 0 )
193 != ( (footprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 )
194 && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
195 {
196 wxString msg;
197 msg.Printf( _( "'%s' settings differ" ), _( "Exclude from bill of materials" ) );
198
199 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
200 drcItem->SetErrorMessage( drcItem->GetErrorMessage( true ) + wxS( ": " ) + msg );
201 drcItem->SetItems( footprint );
202 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
203 }
204
205 // Compare custom fields between schematic component and PCB footprint
206 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_FIELDS_PARITY ) )
207 {
208 std::unordered_map<wxString, wxString> fpFieldsAsMap;
209
210 for( PCB_FIELD* field : footprint->GetFields() )
211 {
212 wxCHECK2( field, continue );
213
214 if( field->IsReference() || field->IsValue() || field->IsComponentClass() )
215 continue;
216
217 fpFieldsAsMap[field->GetName()] = field->GetText();
218 }
219
220 // Remove the extra component fields we don't want to evaluate here
221 nlohmann::ordered_map<wxString, wxString> compFields = component->GetFields();
222 compFields.erase( GetCanonicalFieldName( FIELD_T::REFERENCE ) );
223 compFields.erase( GetCanonicalFieldName( FIELD_T::VALUE ) );
224 compFields.erase( GetCanonicalFieldName( FIELD_T::FOOTPRINT ) );
225 compFields.erase( wxT( "Component Class" ) );
226
227 bool fieldsMatch = true;
228 wxString mismatchDetail;
229
230 for( const auto& [name, value] : compFields )
231 {
232 auto it = fpFieldsAsMap.find( name );
233
234 if( it == fpFieldsAsMap.end() )
235 {
236 fieldsMatch = false;
237 mismatchDetail = wxString::Format( _( "Missing symbol field '%s' in footprint" ), name );
238 break;
239 }
240
241 if( it->second != value )
242 {
243 fieldsMatch = false;
244 mismatchDetail = wxString::Format( _( "Field '%s' differs (PCB: '%s', Schematic: '%s')" ),
245 name, it->second, value );
246 break;
247 }
248 }
249
250 if( !fieldsMatch && !mismatchDetail.IsEmpty() )
251 {
252 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_FIELDS_PARITY );
253
254 drcItem->SetErrorMessage( mismatchDetail );
255 drcItem->SetItems( footprint );
256 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
257 }
258 }
259
260 for( PAD* pad : footprint->Pads() )
261 {
262 if( m_drcEngine->IsErrorLimitExceeded( DRCE_NET_CONFLICT ) )
263 break;
264
265 if( !pad->CanHaveNumber() )
266 continue;
267
268 const COMPONENT_NET& sch_net = component->GetNet( pad->GetNumber() );
269 const wxString& pcb_netname = pad->GetNetname();
270
271 if( !pcb_netname.IsEmpty() && sch_net.GetPinName().IsEmpty() )
272 {
273 wxString msg;
274 msg.Printf( _( "No corresponding pin found in schematic" ) );
275
276 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
277 drcItem->SetErrorMessage( msg );
278 drcItem->SetItems( pad );
279 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
280 }
281 else if( pcb_netname.IsEmpty() && !sch_net.GetNetName().IsEmpty() )
282 {
283 wxString msg;
284 msg.Printf( _( "Pad missing net given by schematic (%s)" ),
285 sch_net.GetNetName() );
286
287 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
288 drcItem->SetErrorMessage( msg );
289 drcItem->SetItems( pad );
290 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
291 }
292 else if( pcb_netname != sch_net.GetNetName()
293 && !( pcb_netname.starts_with(
294 wxT( "unconnected-" ) )
295 && pcb_netname.starts_with( sch_net.GetNetName() ) ) )
296 {
297 wxString msg;
298 msg.Printf( _( "Pad net (%s) doesn't match net given by schematic (%s)" ),
299 pcb_netname,
300 sch_net.GetNetName() );
301
302 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
303 drcItem->SetErrorMessage( msg );
304 drcItem->SetItems( pad );
305 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
306 }
307 }
308
309 for( unsigned jj = 0; jj < component->GetNetCount(); ++jj )
310 {
311 if( m_drcEngine->IsErrorLimitExceeded( DRCE_NET_CONFLICT ) )
312 break;
313
314 const COMPONENT_NET& sch_net = component->GetNet( jj );
315
316 if( !footprint->FindPadByNumber( sch_net.GetPinName() ) )
317 {
318 wxString msg;
319
320 if( sch_net.GetNetName().StartsWith( wxT( "unconnected-" ) ) )
321 {
322 msg = sch_net.GetPinName();
323 }
324 else
325 {
326 msg = wxString::Format( wxT( "%s (%s)" ),
327 sch_net.GetPinName(),
328 sch_net.GetNetName() );
329 }
330
331 msg.Printf( _( "No pad found for pin %s in schematic" ), msg );
332
333 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
334 drcItem->SetErrorMessage( msg );
335 drcItem->SetItems( footprint );
336 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
337 }
338 }
339 }
340 }
341
342 // Search for component footprints found on board but not in netlist.
343 for( FOOTPRINT* footprint : board->Footprints() )
344 {
345 if( m_drcEngine->IsErrorLimitExceeded( DRCE_EXTRA_FOOTPRINT ) )
346 break;
347
348 if( footprint->GetAttributes() & FP_BOARD_ONLY )
349 continue;
350
351 if( !aNetlist.GetComponentByReference( footprint->GetReference() ) )
352 {
353 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_EXTRA_FOOTPRINT );
354
355 drcItem->SetItems( footprint );
356 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
357 }
358 }
359}
360
361
363{
364 if( m_drcEngine->GetTestFootprints() )
365 {
366 if( !reportPhase( _( "Checking PCB to schematic parity..." ) ) )
367 return false;
368
369 auto netlist = m_drcEngine->GetSchematicNetlist();
370
371 if( !netlist )
372 {
373 REPORT_AUX( wxT( "No netlist provided, skipping schematic parity tests." ) );
374 return true;
375 }
376
378 }
379
380 return !m_drcEngine->IsCancelled();
381}
382
383
384namespace detail
385{
387}
const char * name
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
const FOOTPRINTS & Footprints() const
Definition board.h:363
FOOTPRINT * FindFootprintByReference(const wxString &aReference) const
Search for a FOOTPRINT within this board with the given reference designator.
Definition board.cpp:2457
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:405
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:306
int GetAttributes() const
Definition footprint.h:417
const LIB_ID & GetFPID() const
Definition footprint.h:351
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
const wxString & GetValue() const
Definition footprint.h:773
const wxString & GetReference() const
Definition footprint.h:751
VECTOR2I GetPosition() const override
Definition footprint.h:327
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
wxString GetUniStringLibId() const
Definition lib_id.h:148
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:112
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:55
@ DRCE_FOOTPRINT_FILTERS
Definition drc_item.h:80
@ DRCE_SCHEMATIC_FIELDS_PARITY
Definition drc_item.h:120
@ DRCE_DUPLICATE_FOOTPRINT
Definition drc_item.h:76
@ DRCE_EXTRA_FOOTPRINT
Definition drc_item.h:77
@ DRCE_NET_CONFLICT
Definition drc_item.h:78
@ DRCE_MISSING_FOOTPRINT
Definition drc_item.h:75
@ DRCE_SCHEMATIC_PARITY
Definition drc_item.h:79
#define REPORT_AUX(s)
#define _(s)
@ FP_DNP
Definition footprint.h:90
@ FP_BOARD_ONLY
Definition footprint.h:88
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:87
@ UNDEFINED_LAYER
Definition layer_ids.h:61
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ 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".
wxString GetCanonicalFieldName(FIELD_T aFieldType)
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695