KiCad PCB EDA Suite
symbol_checker.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) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
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 <vector>
25#include <sch_symbol.h>
26#include <eda_draw_frame.h>
27
28// helper function to sort pins by pin num
29static bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst );
30
44void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
45 int aGridForPins, EDA_DRAW_FRAME* aUnitsProvider )
46{
47 if( !aSymbol )
48 return;
49
50 wxString msg;
51
52 // Test reference prefix validity:
53 // if the symbol is saved in a library, the prefix should not ends by a digit or a '?'
54 // but it is acceptable if the symbol is saved to aschematic
55 wxString reference_base = aSymbol->GetReferenceField().GetText();
56 wxString illegal_end( wxT( "0123456789?" ) );
57 wxUniChar last_char = reference_base.Last();
58
59 if( illegal_end.Find( last_char ) != wxNOT_FOUND )
60 {
61 msg.Printf( _( "<b>Warning: reference prefix</b><br>prefix ending by '%s' can create"
62 " issues if saved in a symbol library" ),
63 illegal_end );
64 msg += wxT( "<br><br>" );
65 aMessages.push_back( msg );
66 }
67
68 LIB_PINS pinList;
69 aSymbol->GetPins( pinList );
70
71 // Test for duplicates:
72 // Sort pins by pin num, so 2 duplicate pins
73 // (pins with the same number) will be consecutive in list
74 sort( pinList.begin(), pinList.end(), sort_by_pin_number );
75
76 // The minimal grid size allowed to place a pin is 25 mils
77 // the best grid size is 50 mils, but 25 mils is still usable
78 // this is because all aSymbols are using a 50 mils grid to place pins, and therefore
79 // the wires must be on the 50 mils grid
80 // So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
81 const int min_grid_size = schIUScale.MilsToIU( 25 );
82 const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
83
84 for( unsigned ii = 1; ii < pinList.size(); ii++ )
85 {
86 LIB_PIN* pin = pinList[ii - 1];
87 LIB_PIN* next = pinList[ii];
88
89 if( pin->GetNumber() != next->GetNumber() )
90 continue;
91
92 // Pins are not duplicated only if they are in different convert bodies
93 // (but GetConvert() == 0 means commun to all convert bodies)
94 if( pin->GetConvert() != 0 && next->GetConvert() != 0 )
95 {
96 if( pin->GetConvert() != next->GetConvert() )
97 continue;
98 }
99
100 wxString pinName;
101 wxString nextName;
102
103 if( pin->GetName() != "~" && !pin->GetName().IsEmpty() )
104 pinName = " '" + pin->GetName() + "'";
105
106 if( next->GetName() != "~" && !next->GetName().IsEmpty() )
107 nextName = " '" + next->GetName() + "'";
108
109 if( aSymbol->HasConversion() && next->GetConvert() )
110 {
111 if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
112 {
113 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
114 " conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
115 " of converted." ),
116 next->GetNumber(),
117 nextName,
118 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
119 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
120 pin->GetNumber(),
121 pin->GetName(),
122 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
123 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
124 }
125 else
126 {
127 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
128 " conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
129 " in units %s and %s of converted." ),
130 next->GetNumber(),
131 nextName,
132 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
133 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
134 pin->GetNumber(),
135 pinName,
136 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
137 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
138 aSymbol->GetUnitReference( next->GetUnit() ),
139 aSymbol->GetUnitReference( pin->GetUnit() ) );
140 }
141 }
142 else
143 {
144 if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
145 {
146 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
147 " conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
148 next->GetNumber(),
149 nextName,
150 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
151 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
152 pin->GetNumber(),
153 pinName,
154 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
155 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
156 }
157 else
158 {
159 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
160 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
161 " in units %s and %s." ),
162 next->GetNumber(),
163 nextName,
164 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
165 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
166 pin->GetNumber(),
167 pinName,
168 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
169 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
170 aSymbol->GetUnitReference( next->GetUnit() ),
171 aSymbol->GetUnitReference( pin->GetUnit() ) );
172 }
173 }
174
175 msg += wxT( "<br><br>" );
176 aMessages.push_back( msg );
177 }
178
179 // Test for a valid power aSymbol.
180 // A valid power aSymbol has only one unit, no convert and one pin.
181 // And this pin should be PT_POWER_IN (invisible to be automatically connected)
182 // or PT_POWER_OUT for a power flag
183 if( aSymbol->IsPower() )
184 {
185 if( aSymbol->GetUnitCount() != 1 )
186 {
187 msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
188 aMessages.push_back( msg );
189 }
190
191 if( aSymbol->HasConversion() )
192 {
193 msg.Printf( _( "<b>A Power Symbol should have no convert option</b><br><br>" ) );
194 aMessages.push_back( msg );
195 }
196
197 if( pinList.size() != 1 )
198 {
199 msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
200 aMessages.push_back( msg );
201 }
202
203 LIB_PIN* pin = pinList[0];
204
205 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
206 && pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
207 {
208 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
209 "Only a input or output power pin has meaning<br><br>" ) );
210 aMessages.push_back( msg );
211 }
212
213 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && pin->IsVisible() )
214 {
215 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
216 "Only invisible input power pins are automatically connected<br><br>" ) );
217 aMessages.push_back( msg );
218 }
219 }
220
221
222 for( LIB_PIN* pin : pinList )
223 {
224 wxString pinName = pin->GetName();
225
226 if( pinName.IsEmpty() || pinName == "~" )
227 pinName = "";
228 else
229 pinName = "'" + pinName + "'";
230
231 if( !aSymbol->IsPower()
232 && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
233 && !pin->IsVisible() )
234 {
235 // hidden power pin
236 if( aSymbol->HasConversion() && pin->GetConvert() )
237 {
238 if( aSymbol->GetUnitCount() <= 1 )
239 {
240 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
241 " of converted." ),
242 pin->GetNumber(),
243 pinName,
244 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
245 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
246 }
247 else
248 {
249 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
250 " in unit %c of converted." ),
251 pin->GetNumber(),
252 pinName,
253 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
254 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
255 'A' + pin->GetUnit() - 1 );
256 }
257 }
258 else
259 {
260 if( aSymbol->GetUnitCount() <= 1 )
261 {
262 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>." ),
263 pin->GetNumber(),
264 pinName,
265 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
266 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
267 }
268 else
269 {
270 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
271 " in unit %c." ),
272 pin->GetNumber(),
273 pinName,
274 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
275 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
276 'A' + pin->GetUnit() - 1 );
277 }
278 }
279
280 msg += wxT( "<br>" );
281 msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
282 msg += wxT( "<br><br>" );
283 aMessages.push_back( msg );
284 }
285
286 if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
287 || ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
288 {
289 // pin is off grid
290 if( aSymbol->HasConversion() && pin->GetConvert() )
291 {
292 if( aSymbol->GetUnitCount() <= 1 )
293 {
294 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
295 " of converted." ),
296 pin->GetNumber(),
297 pinName,
298 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
299 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
300 }
301 else
302 {
303 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%.3s, %.3s)</b>"
304 " in unit %c of converted." ),
305 pin->GetNumber(),
306 pinName,
307 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
308 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
309 'A' + pin->GetUnit() - 1 );
310 }
311 }
312 else
313 {
314 if( aSymbol->GetUnitCount() <= 1 )
315 {
316 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
317 pin->GetNumber(),
318 pinName,
319 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
320 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
321 }
322 else
323 {
324 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
325 " in unit %c." ),
326 pin->GetNumber(),
327 pinName,
328 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
329 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
330 'A' + pin->GetUnit() - 1 );
331 }
332 }
333
334 msg += wxT( "<br><br>" );
335 aMessages.push_back( msg );
336 }
337 }
338}
339
340
341bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst )
342{
343 // Use number as primary key
344 int test = ref->GetNumber().Cmp( tst->GetNumber() );
345
346 // Use DeMorgan variant as secondary key
347 if( test == 0 )
348 test = ref->GetConvert() - tst->GetConvert();
349
350 // Use unit as tertiary key
351 if( test == 0 )
352 test = ref->GetUnit() - tst->GetUnit();
353
354 return test < 0;
355}
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:111
The base class for create windows for drawing purpose.
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:87
int GetUnit() const
Definition: lib_item.h:273
int GetConvert() const
Definition: lib_item.h:276
const wxString & GetNumber() const
Definition: lib_pin.h:122
Define a library symbol object.
Definition: lib_symbol.h:98
LIB_FIELD & GetReferenceField()
Return reference to the reference designator field.
bool IsPower() const
Definition: lib_symbol.cpp:552
wxString GetUnitReference(int aUnit) override
Return an identifier for aUnit for symbols with units.
Definition: lib_symbol.cpp:391
int GetUnitCount() const override
For items with units, return the number of units.
void GetPins(LIB_PINS &aList, int aUnit=0, int aConvert=0) const
Return a list of pin object pointers from the draw item list.
Definition: lib_symbol.cpp:856
bool HasConversion() const
Test if symbol has more than one body conversion type (DeMorgan).
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
#define _(s)
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_item.h:54
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
CITER next(CITER it)
Definition: ptree.cpp:126
constexpr int MilsToIU(int mils) const
Definition: base_units.h:94
void CheckLibSymbol(LIB_SYMBOL *aSymbol, std::vector< wxString > &aMessages, int aGridForPins, EDA_DRAW_FRAME *aUnitsProvider)
Check a lib symbol to find incorrect settings Pins not on a valid grid Pins duplicated Conflict with ...
static bool sort_by_pin_number(const LIB_PIN *ref, const LIB_PIN *tst)