You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

355 lines
15 KiB

3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019-2023 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. #include <vector>
  24. #include <sch_symbol.h>
  25. #include <eda_draw_frame.h>
  26. // helper function to sort pins by pin num
  27. static bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst );
  28. /**
  29. * Check a lib symbol to find incorrect settings
  30. * Pins not on a valid grid
  31. * Pins duplicated
  32. * Conflict with pins at same location
  33. * Incorrect Power Symbols
  34. * illegal reference prefix (cannot ends by a digit or a '?')
  35. * @param aSymbol is the library symbol to check
  36. * @param aMessages is a room to store error messages
  37. * @param aGridForPins (in IU) is the grid to test pin positions ( >= 25 mils )
  38. * should be 25, 50 or 100 mils (convered to IUs)
  39. * @param aUnitsProvider a frame to format coordinates in messages
  40. */
  41. void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
  42. int aGridForPins, EDA_DRAW_FRAME* aUnitsProvider )
  43. {
  44. if( !aSymbol )
  45. return;
  46. wxString msg;
  47. // Test reference prefix validity:
  48. // if the symbol is saved in a library, the prefix should not ends by a digit or a '?'
  49. // but it is acceptable if the symbol is saved to aschematic
  50. wxString reference_base = aSymbol->GetReferenceField().GetText();
  51. wxString illegal_end( wxT( "0123456789?" ) );
  52. wxUniChar last_char = reference_base.Last();
  53. if( illegal_end.Find( last_char ) != wxNOT_FOUND )
  54. {
  55. msg.Printf( _( "<b>Warning: reference prefix</b><br>prefix ending by '%s' can create"
  56. " issues if saved in a symbol library" ),
  57. illegal_end );
  58. msg += wxT( "<br><br>" );
  59. aMessages.push_back( msg );
  60. }
  61. LIB_PINS pinList;
  62. aSymbol->GetPins( pinList );
  63. // Test for duplicates:
  64. // Sort pins by pin num, so 2 duplicate pins
  65. // (pins with the same number) will be consecutive in list
  66. sort( pinList.begin(), pinList.end(), sort_by_pin_number );
  67. // The minimal grid size allowed to place a pin is 25 mils
  68. // the best grid size is 50 mils, but 25 mils is still usable
  69. // this is because all aSymbols are using a 50 mils grid to place pins, and therefore
  70. // the wires must be on the 50 mils grid
  71. // So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
  72. const int min_grid_size = schIUScale.MilsToIU( 25 );
  73. const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
  74. for( unsigned ii = 1; ii < pinList.size(); ii++ )
  75. {
  76. LIB_PIN* pin = pinList[ii - 1];
  77. LIB_PIN* next = pinList[ii];
  78. if( pin->GetNumber() != next->GetNumber() )
  79. continue;
  80. // Pins are not duplicated only if they are in different convert bodies
  81. // (but GetConvert() == 0 means commun to all convert bodies)
  82. if( pin->GetConvert() != 0 && next->GetConvert() != 0 )
  83. {
  84. if( pin->GetConvert() != next->GetConvert() )
  85. continue;
  86. }
  87. wxString pinName;
  88. wxString nextName;
  89. if( pin->GetName() != "~" && !pin->GetName().IsEmpty() )
  90. pinName = " '" + pin->GetName() + "'";
  91. if( next->GetName() != "~" && !next->GetName().IsEmpty() )
  92. nextName = " '" + next->GetName() + "'";
  93. if( aSymbol->HasConversion() && next->GetConvert() )
  94. {
  95. if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
  96. {
  97. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
  98. " conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
  99. " of converted." ),
  100. next->GetNumber(),
  101. nextName,
  102. aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
  103. aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
  104. pin->GetNumber(),
  105. pin->GetName(),
  106. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  107. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  108. }
  109. else
  110. {
  111. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
  112. " conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
  113. " in units %s and %s of converted." ),
  114. next->GetNumber(),
  115. nextName,
  116. aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
  117. aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
  118. pin->GetNumber(),
  119. pinName,
  120. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  121. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  122. aSymbol->GetUnitReference( next->GetUnit() ),
  123. aSymbol->GetUnitReference( pin->GetUnit() ) );
  124. }
  125. }
  126. else
  127. {
  128. if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
  129. {
  130. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
  131. " conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
  132. next->GetNumber(),
  133. nextName,
  134. aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
  135. aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
  136. pin->GetNumber(),
  137. pinName,
  138. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  139. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  140. }
  141. else
  142. {
  143. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
  144. " conflicts with pin %s%s at location <b>(%s, %s)</b>"
  145. " in units %s and %s." ),
  146. next->GetNumber(),
  147. nextName,
  148. aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
  149. aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
  150. pin->GetNumber(),
  151. pinName,
  152. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  153. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  154. aSymbol->GetUnitReference( next->GetUnit() ),
  155. aSymbol->GetUnitReference( pin->GetUnit() ) );
  156. }
  157. }
  158. msg += wxT( "<br><br>" );
  159. aMessages.push_back( msg );
  160. }
  161. // Test for a valid power aSymbol.
  162. // A valid power aSymbol has only one unit, no convert and one pin.
  163. // And this pin should be PT_POWER_IN (invisible to be automatically connected)
  164. // or PT_POWER_OUT for a power flag
  165. if( aSymbol->IsPower() )
  166. {
  167. if( aSymbol->GetUnitCount() != 1 )
  168. {
  169. msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
  170. aMessages.push_back( msg );
  171. }
  172. if( aSymbol->HasConversion() )
  173. {
  174. msg.Printf( _( "<b>A Power Symbol should have no convert option</b><br><br>" ) );
  175. aMessages.push_back( msg );
  176. }
  177. if( pinList.size() != 1 )
  178. {
  179. msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
  180. aMessages.push_back( msg );
  181. }
  182. LIB_PIN* pin = pinList[0];
  183. if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
  184. && pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
  185. {
  186. msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
  187. "Only an input or output power pin has meaning<br><br>" ) );
  188. aMessages.push_back( msg );
  189. }
  190. if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && pin->IsVisible() )
  191. {
  192. msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
  193. "Only invisible input power pins are automatically connected<br><br>" ) );
  194. aMessages.push_back( msg );
  195. }
  196. }
  197. for( LIB_PIN* pin : pinList )
  198. {
  199. wxString pinName = pin->GetName();
  200. if( pinName.IsEmpty() || pinName == "~" )
  201. pinName = "";
  202. else
  203. pinName = "'" + pinName + "'";
  204. if( !aSymbol->IsPower()
  205. && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
  206. && !pin->IsVisible() )
  207. {
  208. // hidden power pin
  209. if( aSymbol->HasConversion() && pin->GetConvert() )
  210. {
  211. if( aSymbol->GetUnitCount() <= 1 )
  212. {
  213. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
  214. " of converted." ),
  215. pin->GetNumber(),
  216. pinName,
  217. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  218. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  219. }
  220. else
  221. {
  222. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
  223. " in unit %c of converted." ),
  224. pin->GetNumber(),
  225. pinName,
  226. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  227. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  228. 'A' + pin->GetUnit() - 1 );
  229. }
  230. }
  231. else
  232. {
  233. if( aSymbol->GetUnitCount() <= 1 )
  234. {
  235. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>." ),
  236. pin->GetNumber(),
  237. pinName,
  238. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  239. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  240. }
  241. else
  242. {
  243. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
  244. " in unit %c." ),
  245. pin->GetNumber(),
  246. pinName,
  247. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  248. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  249. 'A' + pin->GetUnit() - 1 );
  250. }
  251. }
  252. msg += wxT( "<br>" );
  253. msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
  254. msg += wxT( "<br><br>" );
  255. aMessages.push_back( msg );
  256. }
  257. if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
  258. || ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
  259. {
  260. // pin is off grid
  261. if( aSymbol->HasConversion() && pin->GetConvert() )
  262. {
  263. if( aSymbol->GetUnitCount() <= 1 )
  264. {
  265. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
  266. " of converted." ),
  267. pin->GetNumber(),
  268. pinName,
  269. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  270. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  271. }
  272. else
  273. {
  274. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%.3s, %.3s)</b>"
  275. " in unit %c of converted." ),
  276. pin->GetNumber(),
  277. pinName,
  278. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  279. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  280. 'A' + pin->GetUnit() - 1 );
  281. }
  282. }
  283. else
  284. {
  285. if( aSymbol->GetUnitCount() <= 1 )
  286. {
  287. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
  288. pin->GetNumber(),
  289. pinName,
  290. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  291. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  292. }
  293. else
  294. {
  295. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
  296. " in unit %c." ),
  297. pin->GetNumber(),
  298. pinName,
  299. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  300. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  301. 'A' + pin->GetUnit() - 1 );
  302. }
  303. }
  304. msg += wxT( "<br><br>" );
  305. aMessages.push_back( msg );
  306. }
  307. }
  308. }
  309. bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst )
  310. {
  311. // Use number as primary key
  312. int test = ref->GetNumber().Cmp( tst->GetNumber() );
  313. // Use DeMorgan variant as secondary key
  314. if( test == 0 )
  315. test = ref->GetConvert() - tst->GetConvert();
  316. // Use unit as tertiary key
  317. if( test == 0 )
  318. test = ref->GetUnit() - tst->GetUnit();
  319. return test < 0;
  320. }