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.

1623 lines
58 KiB

2 years ago
3 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 1992-2024 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, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <algorithm>
  26. #include <numeric>
  27. #include "connection_graph.h"
  28. #include "kiface_ids.h"
  29. #include <advanced_config.h>
  30. #include <common.h> // for ExpandEnvVarSubstitutions
  31. #include <erc/erc.h>
  32. #include <erc/erc_sch_pin_context.h>
  33. #include <gal/graphics_abstraction_layer.h>
  34. #include <string_utils.h>
  35. #include <sch_pin.h>
  36. #include <project_sch.h>
  37. #include <project/project_file.h>
  38. #include <project/net_settings.h>
  39. #include <sch_edit_frame.h>
  40. #include <sch_marker.h>
  41. #include <sch_reference_list.h>
  42. #include <sch_rule_area.h>
  43. #include <sch_sheet.h>
  44. #include <sch_sheet_pin.h>
  45. #include <sch_textbox.h>
  46. #include <sch_line.h>
  47. #include <schematic.h>
  48. #include <drawing_sheet/ds_draw_item.h>
  49. #include <drawing_sheet/ds_proxy_view_item.h>
  50. #include <wx/ffile.h>
  51. #include <sim/sim_lib_mgr.h>
  52. #include <progress_reporter.h>
  53. #include <kiway.h>
  54. /* ERC tests :
  55. * 1 - conflicts between connected pins ( example: 2 connected outputs )
  56. * 2 - minimal connections requirements ( 1 input *must* be connected to an
  57. * output, or a passive pin )
  58. */
  59. /*
  60. * Minimal ERC requirements:
  61. * All pins *must* be connected (except ELECTRICAL_PINTYPE::PT_NC).
  62. * When a pin is not connected in schematic, the user must place a "non
  63. * connected" symbol to this pin.
  64. * This ensures a forgotten connection will be detected.
  65. */
  66. // Messages for matrix rows:
  67. const wxString CommentERC_H[] =
  68. {
  69. _( "Input Pin" ),
  70. _( "Output Pin" ),
  71. _( "Bidirectional Pin" ),
  72. _( "Tri-State Pin" ),
  73. _( "Passive Pin" ),
  74. _( "Free Pin" ),
  75. _( "Unspecified Pin" ),
  76. _( "Power Input Pin" ),
  77. _( "Power Output Pin" ),
  78. _( "Open Collector" ),
  79. _( "Open Emitter" ),
  80. _( "No Connection" )
  81. };
  82. // Messages for matrix columns
  83. const wxString CommentERC_V[] =
  84. {
  85. _( "Input Pin" ),
  86. _( "Output Pin" ),
  87. _( "Bidirectional Pin" ),
  88. _( "Tri-State Pin" ),
  89. _( "Passive Pin" ),
  90. _( "Free Pin" ),
  91. _( "Unspecified Pin" ),
  92. _( "Power Input Pin" ),
  93. _( "Power Output Pin" ),
  94. _( "Open Collector" ),
  95. _( "Open Emitter" ),
  96. _( "No Connection" )
  97. };
  98. // List of pin types that are considered drivers for usual input pins
  99. // i.e. pin type = ELECTRICAL_PINTYPE::PT_INPUT, but not PT_POWER_IN
  100. // that need only a PT_POWER_OUT pin type to be driven
  101. const std::set<ELECTRICAL_PINTYPE> DrivingPinTypes =
  102. {
  103. ELECTRICAL_PINTYPE::PT_OUTPUT,
  104. ELECTRICAL_PINTYPE::PT_POWER_OUT,
  105. ELECTRICAL_PINTYPE::PT_PASSIVE,
  106. ELECTRICAL_PINTYPE::PT_TRISTATE,
  107. ELECTRICAL_PINTYPE::PT_BIDI
  108. };
  109. // List of pin types that are considered drivers for power pins
  110. // In fact only a ELECTRICAL_PINTYPE::PT_POWER_OUT pin type can drive
  111. // power input pins
  112. const std::set<ELECTRICAL_PINTYPE> DrivingPowerPinTypes =
  113. {
  114. ELECTRICAL_PINTYPE::PT_POWER_OUT
  115. };
  116. // List of pin types that require a driver elsewhere on the net
  117. const std::set<ELECTRICAL_PINTYPE> DrivenPinTypes =
  118. {
  119. ELECTRICAL_PINTYPE::PT_INPUT,
  120. ELECTRICAL_PINTYPE::PT_POWER_IN
  121. };
  122. extern void CheckDuplicatePins( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
  123. UNITS_PROVIDER* aUnitsProvider );
  124. int ERC_TESTER::TestDuplicateSheetNames( bool aCreateMarker )
  125. {
  126. int err_count = 0;
  127. for( SCH_SCREEN* screen = m_screens.GetFirst(); screen; screen = m_screens.GetNext() )
  128. {
  129. std::vector<SCH_SHEET*> list;
  130. for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
  131. list.push_back( static_cast<SCH_SHEET*>( item ) );
  132. for( size_t i = 0; i < list.size(); i++ )
  133. {
  134. SCH_SHEET* sheet = list[i];
  135. for( size_t j = i + 1; j < list.size(); j++ )
  136. {
  137. SCH_SHEET* test_item = list[j];
  138. // We have found a second sheet: compare names
  139. // we are using case insensitive comparison to avoid mistakes between
  140. // similar names like Mysheet and mysheet
  141. if( sheet->GetShownName( false ).IsSameAs( test_item->GetShownName( false ), false ) )
  142. {
  143. if( aCreateMarker )
  144. {
  145. auto ercItem = ERC_ITEM::Create( ERCE_DUPLICATE_SHEET_NAME );
  146. ercItem->SetItems( sheet, test_item );
  147. SCH_MARKER* marker = new SCH_MARKER( ercItem, sheet->GetPosition() );
  148. screen->Append( marker );
  149. }
  150. err_count++;
  151. }
  152. }
  153. }
  154. }
  155. return err_count;
  156. }
  157. void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
  158. {
  159. DS_DRAW_ITEM_LIST wsItems( schIUScale );
  160. auto unresolved =
  161. [this]( wxString str )
  162. {
  163. str = ExpandEnvVarSubstitutions( str, &m_schematic->Prj() );
  164. return str.Matches( wxS( "*${*}*" ) );
  165. };
  166. auto testAssertion =
  167. []( const SCH_ITEM* item, const SCH_SHEET_PATH& sheet, SCH_SCREEN* screen,
  168. const wxString& text )
  169. {
  170. static wxRegEx warningExpr( wxS( "^\\$\\{ERC_WARNING\\s*([^}]*)\\}(.*)$" ) );
  171. static wxRegEx errorExpr( wxS( "^\\$\\{ERC_ERROR\\s*([^}]*)\\}(.*)$" ) );
  172. if( warningExpr.Matches( text ) )
  173. {
  174. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_GENERIC_WARNING );
  175. ercItem->SetItems( item );
  176. ercItem->SetSheetSpecificPath( sheet );
  177. ercItem->SetErrorMessage( warningExpr.GetMatch( text, 1 ) );
  178. SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
  179. screen->Append( marker );
  180. }
  181. if( errorExpr.Matches( text ) )
  182. {
  183. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_GENERIC_ERROR );
  184. ercItem->SetItems( item );
  185. ercItem->SetSheetSpecificPath( sheet );
  186. ercItem->SetErrorMessage( errorExpr.GetMatch( text, 1 ) );
  187. SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
  188. screen->Append( marker );
  189. }
  190. };
  191. if( aDrawingSheet )
  192. {
  193. wsItems.SetPageNumber( wxS( "1" ) );
  194. wsItems.SetSheetCount( 1 );
  195. wsItems.SetFileName( wxS( "dummyFilename" ) );
  196. wsItems.SetSheetName( wxS( "dummySheet" ) );
  197. wsItems.SetSheetLayer( wxS( "dummyLayer" ) );
  198. wsItems.SetProject( &m_schematic->Prj() );
  199. wsItems.BuildDrawItemsList( aDrawingSheet->GetPageInfo(), aDrawingSheet->GetTitleBlock() );
  200. }
  201. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  202. {
  203. SCH_SCREEN* screen = sheet.LastScreen();
  204. for( SCH_ITEM* item : screen->Items().OfType( SCH_LOCATE_ANY_T ) )
  205. {
  206. if( item->Type() == SCH_SYMBOL_T )
  207. {
  208. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  209. for( SCH_FIELD& field : symbol->GetFields() )
  210. {
  211. if( unresolved( field.GetShownText( &sheet, true ) ) )
  212. {
  213. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  214. ercItem->SetItems( symbol );
  215. ercItem->SetSheetSpecificPath( sheet );
  216. SCH_MARKER* marker = new SCH_MARKER( ercItem, field.GetPosition() );
  217. screen->Append( marker );
  218. }
  219. testAssertion( &field, sheet, screen, field.GetText() );
  220. }
  221. symbol->GetLibSymbolRef()->RunOnChildren(
  222. [&]( SCH_ITEM* child )
  223. {
  224. if( child->Type() == SCH_FIELD_T )
  225. {
  226. // test only SCH_SYMBOL fields, not LIB_SYMBOL fields
  227. }
  228. else if( child->Type() == SCH_TEXT_T )
  229. {
  230. SCH_TEXT* textItem = static_cast<SCH_TEXT*>( child );
  231. if( unresolved( textItem->GetShownText( &sheet, true ) ) )
  232. {
  233. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  234. ercItem->SetItems( symbol );
  235. ercItem->SetSheetSpecificPath( sheet );
  236. BOX2I bbox = textItem->GetBoundingBox();
  237. bbox = symbol->GetTransform().TransformCoordinate( bbox );
  238. VECTOR2I pos = bbox.Centre() + symbol->GetPosition();
  239. SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
  240. screen->Append( marker );
  241. }
  242. testAssertion( symbol, sheet, screen, textItem->GetText() );
  243. }
  244. else if( child->Type() == SCH_TEXTBOX_T )
  245. {
  246. SCH_TEXTBOX* textboxItem = static_cast<SCH_TEXTBOX*>( child );
  247. if( unresolved( textboxItem->GetShownText( &sheet, true ) ) )
  248. {
  249. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  250. ercItem->SetItems( symbol );
  251. ercItem->SetSheetSpecificPath( sheet );
  252. BOX2I bbox = textboxItem->GetBoundingBox();
  253. bbox = symbol->GetTransform().TransformCoordinate( bbox );
  254. VECTOR2I pos = bbox.Centre() + symbol->GetPosition();
  255. SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
  256. screen->Append( marker );
  257. }
  258. testAssertion( symbol, sheet, screen, textboxItem->GetText() );
  259. }
  260. } );
  261. }
  262. else if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
  263. {
  264. for( SCH_FIELD& field : label->GetFields() )
  265. {
  266. if( unresolved( field.GetShownText( &sheet, true ) ) )
  267. {
  268. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  269. ercItem->SetItems( label );
  270. ercItem->SetSheetSpecificPath( sheet );
  271. SCH_MARKER* marker = new SCH_MARKER( ercItem, field.GetPosition() );
  272. screen->Append( marker );
  273. }
  274. testAssertion( &field, sheet, screen, field.GetText() );
  275. }
  276. }
  277. else if( item->Type() == SCH_SHEET_T )
  278. {
  279. SCH_SHEET* subSheet = static_cast<SCH_SHEET*>( item );
  280. for( SCH_FIELD& field : subSheet->GetFields() )
  281. {
  282. if( unresolved( field.GetShownText( &sheet, true ) ) )
  283. {
  284. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  285. ercItem->SetItems( subSheet );
  286. ercItem->SetSheetSpecificPath( sheet );
  287. SCH_MARKER* marker = new SCH_MARKER( ercItem, field.GetPosition() );
  288. screen->Append( marker );
  289. }
  290. testAssertion( &field, sheet, screen, field.GetText() );
  291. }
  292. SCH_SHEET_PATH subSheetPath = sheet;
  293. subSheetPath.push_back( subSheet );
  294. for( SCH_SHEET_PIN* pin : subSheet->GetPins() )
  295. {
  296. if( pin->GetShownText( &subSheetPath, true ).Matches( wxS( "*${*}*" ) ) )
  297. {
  298. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  299. ercItem->SetItems( pin );
  300. ercItem->SetSheetSpecificPath( sheet );
  301. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
  302. screen->Append( marker );
  303. }
  304. }
  305. }
  306. else if( SCH_TEXT* text = dynamic_cast<SCH_TEXT*>( item ) )
  307. {
  308. if( text->GetShownText( &sheet, true ).Matches( wxS( "*${*}*" ) ) )
  309. {
  310. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  311. ercItem->SetItems( text );
  312. ercItem->SetSheetSpecificPath( sheet );
  313. SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
  314. screen->Append( marker );
  315. }
  316. testAssertion( text, sheet, screen, text->GetText() );
  317. }
  318. else if( SCH_TEXTBOX* textBox = dynamic_cast<SCH_TEXTBOX*>( item ) )
  319. {
  320. if( textBox->GetShownText( &sheet, true ).Matches( wxS( "*${*}*" ) ) )
  321. {
  322. auto ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  323. ercItem->SetItems( textBox );
  324. ercItem->SetSheetSpecificPath( sheet );
  325. SCH_MARKER* marker = new SCH_MARKER( ercItem, textBox->GetPosition() );
  326. screen->Append( marker );
  327. }
  328. testAssertion( textBox, sheet, screen, textBox->GetText() );
  329. }
  330. }
  331. for( DS_DRAW_ITEM_BASE* item = wsItems.GetFirst(); item; item = wsItems.GetNext() )
  332. {
  333. if( DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item ) )
  334. {
  335. if( text->GetShownText( true ).Matches( wxS( "*${*}*" ) ) )
  336. {
  337. std::shared_ptr<ERC_ITEM> erc = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  338. erc->SetErrorMessage( _( "Unresolved text variable in drawing sheet" ) );
  339. erc->SetSheetSpecificPath( sheet );
  340. SCH_MARKER* marker = new SCH_MARKER( erc, text->GetPosition() );
  341. screen->Append( marker );
  342. }
  343. }
  344. }
  345. }
  346. }
  347. int ERC_TESTER::TestConflictingBusAliases()
  348. {
  349. wxString msg;
  350. int err_count = 0;
  351. std::vector<std::shared_ptr<BUS_ALIAS>> aliases;
  352. for( SCH_SCREEN* screen = m_screens.GetFirst(); screen; screen = m_screens.GetNext() )
  353. {
  354. const auto& screen_aliases = screen->GetBusAliases();
  355. for( const std::shared_ptr<BUS_ALIAS>& alias : screen_aliases )
  356. {
  357. std::vector<wxString> aliasMembers = alias->Members();
  358. std::sort( aliasMembers.begin(), aliasMembers.end() );
  359. for( const std::shared_ptr<BUS_ALIAS>& test : aliases )
  360. {
  361. std::vector<wxString> testMembers = test->Members();
  362. std::sort( testMembers.begin(), testMembers.end() );
  363. if( alias->GetName() == test->GetName() && aliasMembers != testMembers )
  364. {
  365. msg.Printf( _( "Bus alias %s has conflicting definitions on %s and %s" ),
  366. alias->GetName(),
  367. alias->GetParent()->GetFileName(),
  368. test->GetParent()->GetFileName() );
  369. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ALIAS_CONFLICT );
  370. ercItem->SetErrorMessage( msg );
  371. SCH_MARKER* marker = new SCH_MARKER( ercItem, VECTOR2I() );
  372. test->GetParent()->Append( marker );
  373. ++err_count;
  374. }
  375. }
  376. }
  377. aliases.insert( aliases.end(), screen_aliases.begin(), screen_aliases.end() );
  378. }
  379. return err_count;
  380. }
  381. int ERC_TESTER::TestMultiunitFootprints()
  382. {
  383. int errors = 0;
  384. for( std::pair<const wxString, SCH_REFERENCE_LIST>& symbol : m_refMap )
  385. {
  386. SCH_REFERENCE_LIST& refList = symbol.second;
  387. if( refList.GetCount() == 0 )
  388. {
  389. wxFAIL; // it should not happen
  390. continue;
  391. }
  392. // Reference footprint
  393. SCH_SYMBOL* unit = nullptr;
  394. wxString unitName;
  395. wxString unitFP;
  396. for( size_t ii = 0; ii < refList.GetCount(); ++ii )
  397. {
  398. SCH_SHEET_PATH sheetPath = refList.GetItem( ii ).GetSheetPath();
  399. unitFP = refList.GetItem( ii ).GetFootprint();
  400. if( !unitFP.IsEmpty() )
  401. {
  402. unit = refList.GetItem( ii ).GetSymbol();
  403. unitName = unit->GetRef( &sheetPath, true );
  404. break;
  405. }
  406. }
  407. for( size_t ii = 0; ii < refList.GetCount(); ++ii )
  408. {
  409. SCH_REFERENCE& secondRef = refList.GetItem( ii );
  410. SCH_SYMBOL* secondUnit = secondRef.GetSymbol();
  411. wxString secondName = secondUnit->GetRef( &secondRef.GetSheetPath(), true );
  412. const wxString secondFp = secondRef.GetFootprint();
  413. wxString msg;
  414. if( unit && !secondFp.IsEmpty() && unitFP != secondFp )
  415. {
  416. msg.Printf( _( "Different footprints assigned to %s and %s" ),
  417. unitName, secondName );
  418. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DIFFERENT_UNIT_FP );
  419. ercItem->SetErrorMessage( msg );
  420. ercItem->SetItems( unit, secondUnit );
  421. SCH_MARKER* marker = new SCH_MARKER( ercItem, secondUnit->GetPosition() );
  422. secondRef.GetSheetPath().LastScreen()->Append( marker );
  423. ++errors;
  424. }
  425. }
  426. }
  427. return errors;
  428. }
  429. int ERC_TESTER::TestMissingUnits()
  430. {
  431. int errors = 0;
  432. for( std::pair<const wxString, SCH_REFERENCE_LIST>& symbol : m_refMap )
  433. {
  434. SCH_REFERENCE_LIST& refList = symbol.second;
  435. wxCHECK2( refList.GetCount(), continue );
  436. // Reference unit
  437. SCH_REFERENCE& base_ref = refList.GetItem( 0 );
  438. SCH_SYMBOL* unit = base_ref.GetSymbol();
  439. LIB_SYMBOL* libSymbol = base_ref.GetLibPart();
  440. if( static_cast<ssize_t>( refList.GetCount() ) == libSymbol->GetUnitCount() )
  441. continue;
  442. std::set<int> lib_units;
  443. std::set<int> instance_units;
  444. std::set<int> missing_units;
  445. auto report =
  446. [&]( std::set<int>& aMissingUnits, const wxString& aErrorMsg, int aErrorCode )
  447. {
  448. wxString msg;
  449. wxString missing_pin_units = wxS( "[ " );
  450. int ii = 0;
  451. for( int missing_unit : aMissingUnits )
  452. {
  453. if( ii++ == 3 )
  454. {
  455. missing_pin_units += wxS( "....." );
  456. break;
  457. }
  458. missing_pin_units += libSymbol->GetUnitDisplayName( missing_unit ) + ", " ;
  459. }
  460. missing_pin_units.Truncate( missing_pin_units.length() - 2 );
  461. missing_pin_units += wxS( " ]" );
  462. msg.Printf( aErrorMsg, symbol.first, missing_pin_units );
  463. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( aErrorCode );
  464. ercItem->SetErrorMessage( msg );
  465. ercItem->SetItems( unit );
  466. SCH_MARKER* marker = new SCH_MARKER( ercItem, unit->GetPosition() );
  467. base_ref.GetSheetPath().LastScreen()->Append( marker );
  468. ++errors;
  469. };
  470. for( int ii = 1; ii <= libSymbol->GetUnitCount(); ++ii )
  471. lib_units.insert( lib_units.end(), ii );
  472. for( size_t ii = 0; ii < refList.GetCount(); ++ii )
  473. instance_units.insert( instance_units.end(), refList.GetItem( ii ).GetUnit() );
  474. std::set_difference( lib_units.begin(), lib_units.end(),
  475. instance_units.begin(), instance_units.end(),
  476. std::inserter( missing_units, missing_units.begin() ) );
  477. if( !missing_units.empty() && m_settings.IsTestEnabled( ERCE_MISSING_UNIT ) )
  478. {
  479. report( missing_units, _( "Symbol %s has unplaced units %s" ), ERCE_MISSING_UNIT );
  480. }
  481. std::set<int> missing_power;
  482. std::set<int> missing_input;
  483. std::set<int> missing_bidi;
  484. for( int missing_unit : missing_units )
  485. {
  486. int bodyStyle = 0;
  487. for( size_t ii = 0; ii < refList.GetCount(); ++ii )
  488. {
  489. if( refList.GetItem( ii ).GetUnit() == missing_unit )
  490. {
  491. bodyStyle = refList.GetItem( ii ).GetSymbol()->GetBodyStyle();
  492. break;
  493. }
  494. }
  495. for( SCH_PIN* pin : libSymbol->GetPins( missing_unit, bodyStyle ) )
  496. {
  497. switch( pin->GetType() )
  498. {
  499. case ELECTRICAL_PINTYPE::PT_POWER_IN:
  500. missing_power.insert( missing_unit );
  501. break;
  502. case ELECTRICAL_PINTYPE::PT_BIDI:
  503. missing_bidi.insert( missing_unit );
  504. break;
  505. case ELECTRICAL_PINTYPE::PT_INPUT:
  506. missing_input.insert( missing_unit );
  507. break;
  508. default:
  509. break;
  510. }
  511. }
  512. }
  513. if( !missing_power.empty() && m_settings.IsTestEnabled( ERCE_MISSING_POWER_INPUT_PIN ) )
  514. {
  515. report( missing_power,
  516. _( "Symbol %s has input power pins in units %s that are not placed." ),
  517. ERCE_MISSING_POWER_INPUT_PIN );
  518. }
  519. if( !missing_input.empty() && m_settings.IsTestEnabled( ERCE_MISSING_INPUT_PIN ) )
  520. {
  521. report( missing_input,
  522. _( "Symbol %s has input pins in units %s that are not placed." ),
  523. ERCE_MISSING_INPUT_PIN );
  524. }
  525. if( !missing_bidi.empty() && m_settings.IsTestEnabled( ERCE_MISSING_BIDI_PIN ) )
  526. {
  527. report( missing_bidi,
  528. _( "Symbol %s has bidirectional pins in units %s that are not placed." ),
  529. ERCE_MISSING_BIDI_PIN );
  530. }
  531. }
  532. return errors;
  533. }
  534. int ERC_TESTER::TestMissingNetclasses()
  535. {
  536. int err_count = 0;
  537. std::shared_ptr<NET_SETTINGS>& settings = m_schematic->Prj().GetProjectFile().NetSettings();
  538. wxString defaultNetclass = settings->m_DefaultNetClass->GetName();
  539. auto logError =
  540. [&]( const SCH_SHEET_PATH& sheet, SCH_ITEM* item, const wxString& netclass )
  541. {
  542. err_count++;
  543. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNDEFINED_NETCLASS );
  544. ercItem->SetItems( item );
  545. ercItem->SetErrorMessage( wxString::Format( _( "Netclass %s is not defined" ),
  546. netclass ) );
  547. SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
  548. sheet.LastScreen()->Append( marker );
  549. };
  550. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  551. {
  552. for( SCH_ITEM* item : sheet.LastScreen()->Items() )
  553. {
  554. item->RunOnChildren(
  555. [&]( SCH_ITEM* aChild )
  556. {
  557. if( aChild->Type() == SCH_FIELD_T )
  558. {
  559. SCH_FIELD* field = static_cast<SCH_FIELD*>( aChild );
  560. if( field->GetCanonicalName() == wxT( "Netclass" ) )
  561. {
  562. wxString netclass = field->GetShownText( &sheet, false );
  563. if( !netclass.IsSameAs( defaultNetclass )
  564. && settings->m_NetClasses.count( netclass ) == 0 )
  565. {
  566. logError( sheet, item, netclass );
  567. }
  568. }
  569. }
  570. return true;
  571. } );
  572. }
  573. }
  574. return err_count;
  575. }
  576. int ERC_TESTER::TestFourWayJunction()
  577. {
  578. int err_count = 0;
  579. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  580. {
  581. std::map<VECTOR2I, std::vector<SCH_ITEM*>> connMap;
  582. SCH_SCREEN* screen = sheet.LastScreen();
  583. for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
  584. {
  585. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  586. for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
  587. connMap[pin->GetPosition()].emplace_back( pin );
  588. }
  589. for( SCH_ITEM* item : screen->Items().OfType( SCH_LINE_T ) )
  590. {
  591. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  592. if( line->IsGraphicLine() )
  593. continue;
  594. for( const VECTOR2I& pt : line->GetConnectionPoints() )
  595. connMap[pt].emplace_back( line );
  596. }
  597. for( const std::pair<const VECTOR2I, std::vector<SCH_ITEM*>>& pair : connMap )
  598. {
  599. if( pair.second.size() >= 4 )
  600. {
  601. err_count++;
  602. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_FOUR_WAY_JUNCTION );
  603. ercItem->SetItems( pair.second[0], pair.second[1], pair.second[2], pair.second[3] );
  604. wxString msg = wxString::Format( _( "Four items connected at %d, %d" ),
  605. pair.first.x, pair.first.y );
  606. ercItem->SetErrorMessage( msg );
  607. ercItem->SetSheetSpecificPath( sheet );
  608. SCH_MARKER* marker = new SCH_MARKER( ercItem, pair.first );
  609. sheet.LastScreen()->Append( marker );
  610. }
  611. }
  612. }
  613. return err_count;
  614. }
  615. int ERC_TESTER::TestNoConnectPins()
  616. {
  617. int err_count = 0;
  618. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  619. {
  620. std::map<VECTOR2I, std::vector<SCH_ITEM*>> pinMap;
  621. auto addOther =
  622. [&]( const VECTOR2I& pt, SCH_ITEM* aOther )
  623. {
  624. if( pinMap.count( pt ) )
  625. pinMap[pt].emplace_back( aOther );
  626. };
  627. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  628. {
  629. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  630. for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
  631. {
  632. if( pin->GetLibPin()->GetType() == ELECTRICAL_PINTYPE::PT_NC )
  633. pinMap[pin->GetPosition()].emplace_back( pin );
  634. }
  635. }
  636. for( SCH_ITEM* item : sheet.LastScreen()->Items() )
  637. {
  638. if( item->Type() == SCH_SYMBOL_T )
  639. {
  640. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  641. for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
  642. {
  643. if( pin->GetLibPin()->GetType() != ELECTRICAL_PINTYPE::PT_NC )
  644. addOther( pin->GetPosition(), pin );
  645. }
  646. }
  647. else if( item->IsConnectable() )
  648. {
  649. for( const VECTOR2I& pt : item->GetConnectionPoints() )
  650. addOther( pt, item );
  651. }
  652. }
  653. for( const std::pair<const VECTOR2I, std::vector<SCH_ITEM*>>& pair : pinMap )
  654. {
  655. if( pair.second.size() > 1 )
  656. {
  657. err_count++;
  658. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
  659. ercItem->SetItems( pair.second[0], pair.second[1],
  660. pair.second.size() > 2 ? pair.second[2] : nullptr,
  661. pair.second.size() > 3 ? pair.second[3] : nullptr );
  662. ercItem->SetErrorMessage( _( "Pin with 'no connection' type is connected" ) );
  663. ercItem->SetSheetSpecificPath( sheet );
  664. SCH_MARKER* marker = new SCH_MARKER( ercItem, pair.first );
  665. sheet.LastScreen()->Append( marker );
  666. }
  667. }
  668. }
  669. return err_count;
  670. }
  671. int ERC_TESTER::TestPinToPin()
  672. {
  673. int errors = 0;
  674. for( const std::pair<NET_NAME_CODE_CACHE_KEY, std::vector<CONNECTION_SUBGRAPH*>> net : m_nets )
  675. {
  676. std::vector<ERC_SCH_PIN_CONTEXT> pins;
  677. std::unordered_map<EDA_ITEM*, SCH_SCREEN*> pinToScreenMap;
  678. bool has_noconnect = false;
  679. for( CONNECTION_SUBGRAPH* subgraph: net.second )
  680. {
  681. if( subgraph->GetNoConnect() )
  682. has_noconnect = true;
  683. for( SCH_ITEM* item : subgraph->GetItems() )
  684. {
  685. if( item->Type() == SCH_PIN_T )
  686. {
  687. pins.emplace_back( static_cast<SCH_PIN*>( item ), subgraph->GetSheet() );
  688. pinToScreenMap[item] = subgraph->GetSheet().LastScreen();
  689. }
  690. }
  691. }
  692. std::sort( pins.begin(), pins.end(),
  693. []( const ERC_SCH_PIN_CONTEXT& lhs, const ERC_SCH_PIN_CONTEXT& rhs )
  694. {
  695. int ret = StrNumCmp( lhs.Pin()->GetParentSymbol()->GetRef( &lhs.Sheet() ),
  696. rhs.Pin()->GetParentSymbol()->GetRef( &rhs.Sheet() ) );
  697. if( ret == 0 )
  698. ret = StrNumCmp( lhs.Pin()->GetNumber(), rhs.Pin()->GetNumber() );
  699. if( ret == 0 )
  700. ret = lhs < rhs; // Fallback to hash to guarantee deterministic sort
  701. return ret < 0;
  702. } );
  703. ERC_SCH_PIN_CONTEXT needsDriver;
  704. ELECTRICAL_PINTYPE needsDriverType;
  705. bool hasDriver = false;
  706. // We need different drivers for power nets and normal nets.
  707. // A power net has at least one pin having the ELECTRICAL_PINTYPE::PT_POWER_IN
  708. // and power nets can be driven only by ELECTRICAL_PINTYPE::PT_POWER_OUT pins
  709. bool ispowerNet = false;
  710. for( ERC_SCH_PIN_CONTEXT& refPin : pins )
  711. {
  712. if( refPin.Pin()->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN )
  713. {
  714. ispowerNet = true;
  715. break;
  716. }
  717. }
  718. for( auto refIt = pins.begin(); refIt != pins.end(); ++refIt )
  719. {
  720. ERC_SCH_PIN_CONTEXT& refPin = *refIt;
  721. ELECTRICAL_PINTYPE refType = refPin.Pin()->GetType();
  722. if( DrivenPinTypes.contains( refType ) )
  723. {
  724. // needsDriver will be the pin shown in the error report eventually, so try to
  725. // upgrade to a "better" pin if possible: something visible and only a power symbol
  726. // if this net needs a power driver
  727. if( !needsDriver.Pin()
  728. || ( !needsDriver.Pin()->IsVisible() && refPin.Pin()->IsVisible() )
  729. || ( ispowerNet != ( needsDriverType == ELECTRICAL_PINTYPE::PT_POWER_IN )
  730. && ispowerNet == ( refType == ELECTRICAL_PINTYPE::PT_POWER_IN ) ) )
  731. {
  732. needsDriver = refPin;
  733. needsDriverType = needsDriver.Pin()->GetType();
  734. }
  735. }
  736. if( ispowerNet )
  737. hasDriver |= ( DrivingPowerPinTypes.count( refType ) != 0 );
  738. else
  739. hasDriver |= ( DrivingPinTypes.count( refType ) != 0 );
  740. for( auto testIt = refIt + 1; testIt != pins.end(); ++testIt )
  741. {
  742. ERC_SCH_PIN_CONTEXT& testPin = *testIt;
  743. // Multiple pins in the same symbol that share a type,
  744. // name and position are considered
  745. // "stacked" and shouldn't trigger ERC errors
  746. if( refPin.Pin()->IsStacked( testPin.Pin() ) && refPin.Sheet() == testPin.Sheet() )
  747. continue;
  748. ELECTRICAL_PINTYPE testType = testPin.Pin()->GetType();
  749. if( ispowerNet )
  750. hasDriver |= DrivingPowerPinTypes.contains( testType );
  751. else
  752. hasDriver |= DrivingPinTypes.contains( testType );
  753. PIN_ERROR erc = m_settings.GetPinMapValue( refType, testType );
  754. if( erc != PIN_ERROR::OK && m_settings.IsTestEnabled( ERCE_PIN_TO_PIN_WARNING ) )
  755. {
  756. std::shared_ptr<ERC_ITEM> ercItem =
  757. ERC_ITEM::Create( erc == PIN_ERROR::WARNING ? ERCE_PIN_TO_PIN_WARNING :
  758. ERCE_PIN_TO_PIN_ERROR );
  759. ercItem->SetItems( refPin.Pin(), testPin.Pin() );
  760. ercItem->SetSheetSpecificPath( refPin.Sheet() );
  761. ercItem->SetItemsSheetPaths( refPin.Sheet(), testPin.Sheet() );
  762. ercItem->SetErrorMessage(
  763. wxString::Format( _( "Pins of type %s and %s are connected" ),
  764. ElectricalPinTypeGetText( refType ),
  765. ElectricalPinTypeGetText( testType ) ) );
  766. SCH_MARKER* marker = new SCH_MARKER( ercItem, refPin.Pin()->GetPosition() );
  767. pinToScreenMap[refPin.Pin()]->Append( marker );
  768. errors++;
  769. }
  770. }
  771. }
  772. if( needsDriver.Pin() && !hasDriver && !has_noconnect )
  773. {
  774. int err_code = ispowerNet ? ERCE_POWERPIN_NOT_DRIVEN : ERCE_PIN_NOT_DRIVEN;
  775. if( m_settings.IsTestEnabled( err_code ) )
  776. {
  777. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( err_code );
  778. ercItem->SetItems( needsDriver.Pin() );
  779. ercItem->SetSheetSpecificPath( needsDriver.Sheet() );
  780. ercItem->SetItemsSheetPaths( needsDriver.Sheet() );
  781. SCH_MARKER* marker = new SCH_MARKER( ercItem, needsDriver.Pin()->GetPosition() );
  782. pinToScreenMap[needsDriver.Pin()]->Append( marker );
  783. errors++;
  784. }
  785. }
  786. }
  787. return errors;
  788. }
  789. int ERC_TESTER::TestMultUnitPinConflicts()
  790. {
  791. int errors = 0;
  792. std::unordered_map<wxString, std::pair<wxString, SCH_PIN*>> pinToNetMap;
  793. for( const std::pair<NET_NAME_CODE_CACHE_KEY, std::vector<CONNECTION_SUBGRAPH*>> net : m_nets )
  794. {
  795. const wxString& netName = net.first.Name;
  796. for( CONNECTION_SUBGRAPH* subgraph : net.second )
  797. {
  798. for( SCH_ITEM* item : subgraph->GetItems() )
  799. {
  800. if( item->Type() == SCH_PIN_T )
  801. {
  802. SCH_PIN* pin = static_cast<SCH_PIN*>( item );
  803. const SCH_SHEET_PATH& sheet = subgraph->GetSheet();
  804. if( !pin->GetLibPin()->GetParentSymbol()->IsMulti() )
  805. continue;
  806. wxString name = pin->GetParentSymbol()->GetRef( &sheet ) +
  807. + ":" + pin->GetShownNumber();
  808. if( !pinToNetMap.count( name ) )
  809. {
  810. pinToNetMap[name] = std::make_pair( netName, pin );
  811. }
  812. else if( pinToNetMap[name].first != netName )
  813. {
  814. std::shared_ptr<ERC_ITEM> ercItem =
  815. ERC_ITEM::Create( ERCE_DIFFERENT_UNIT_NET );
  816. ercItem->SetErrorMessage( wxString::Format(
  817. _( "Pin %s is connected to both %s and %s" ),
  818. pin->GetShownNumber(),
  819. netName,
  820. pinToNetMap[name].first ) );
  821. ercItem->SetItems( pin, pinToNetMap[name].second );
  822. ercItem->SetSheetSpecificPath( sheet );
  823. ercItem->SetItemsSheetPaths( sheet, sheet );
  824. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
  825. sheet.LastScreen()->Append( marker );
  826. errors += 1;
  827. }
  828. }
  829. }
  830. }
  831. }
  832. return errors;
  833. }
  834. int ERC_TESTER::TestSimilarLabels()
  835. {
  836. int errors = 0;
  837. std::unordered_map<wxString, std::pair<SCH_LABEL_BASE*, SCH_SHEET_PATH>> labelMap;
  838. for( const std::pair<NET_NAME_CODE_CACHE_KEY, std::vector<CONNECTION_SUBGRAPH*>> net : m_nets )
  839. {
  840. for( CONNECTION_SUBGRAPH* subgraph : net.second )
  841. {
  842. const SCH_SHEET_PATH& sheet = subgraph->GetSheet();
  843. for( SCH_ITEM* item : subgraph->GetItems() )
  844. {
  845. switch( item->Type() )
  846. {
  847. case SCH_LABEL_T:
  848. case SCH_HIER_LABEL_T:
  849. case SCH_GLOBAL_LABEL_T:
  850. {
  851. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
  852. wxString normalized = label->GetShownText( &sheet, false ).Lower();
  853. if( !labelMap.count( normalized ) )
  854. {
  855. labelMap[normalized] = std::make_pair( label, sheet );
  856. break;
  857. }
  858. auto& [ otherLabel, otherSheet ] = labelMap.at( normalized );
  859. if( otherLabel->GetShownText( &otherSheet, false )
  860. != label->GetShownText( &sheet, false ) )
  861. {
  862. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SIMILAR_LABELS );
  863. ercItem->SetItems( label, labelMap.at( normalized ).first );
  864. ercItem->SetSheetSpecificPath( sheet );
  865. ercItem->SetItemsSheetPaths( sheet, labelMap.at( normalized ).second );
  866. SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
  867. sheet.LastScreen()->Append( marker );
  868. errors += 1;
  869. }
  870. break;
  871. }
  872. default:
  873. break;
  874. }
  875. }
  876. }
  877. }
  878. return errors;
  879. }
  880. int ERC_TESTER::TestLibSymbolIssues()
  881. {
  882. wxCHECK( m_schematic, 0 );
  883. SYMBOL_LIB_TABLE* libTable = PROJECT_SCH::SchSymbolLibTable( &m_schematic->Prj() );
  884. wxString msg;
  885. int err_count = 0;
  886. for( SCH_SCREEN* screen = m_screens.GetFirst(); screen; screen = m_screens.GetNext() )
  887. {
  888. std::vector<SCH_MARKER*> markers;
  889. for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
  890. {
  891. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  892. LIB_SYMBOL* libSymbolInSchematic = symbol->GetLibSymbolRef().get();
  893. wxCHECK2( libSymbolInSchematic, continue );
  894. wxString libName = symbol->GetLibId().GetLibNickname();
  895. LIB_TABLE_ROW* libTableRow = libTable->FindRow( libName, true );
  896. if( !libTableRow )
  897. {
  898. if( m_settings.IsTestEnabled( ERCE_LIB_SYMBOL_ISSUES ) )
  899. {
  900. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
  901. ercItem->SetItems( symbol );
  902. msg.Printf( _( "The current configuration does not include the symbol library '%s'" ),
  903. UnescapeString( libName ) );
  904. ercItem->SetErrorMessage( msg );
  905. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  906. }
  907. continue;
  908. }
  909. else if( !libTable->HasLibrary( libName, true ) )
  910. {
  911. if( m_settings.IsTestEnabled( ERCE_LIB_SYMBOL_ISSUES ) )
  912. {
  913. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
  914. ercItem->SetItems( symbol );
  915. msg.Printf( _( "The library '%s' is not enabled in the current configuration" ),
  916. UnescapeString( libName ) );
  917. ercItem->SetErrorMessage( msg );
  918. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  919. }
  920. continue;
  921. }
  922. wxString symbolName = symbol->GetLibId().GetLibItemName();
  923. LIB_SYMBOL* libSymbol = SchGetLibSymbol( symbol->GetLibId(), libTable );
  924. if( libSymbol == nullptr )
  925. {
  926. if( m_settings.IsTestEnabled( ERCE_LIB_SYMBOL_ISSUES ) )
  927. {
  928. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
  929. ercItem->SetItems( symbol );
  930. msg.Printf( _( "Symbol '%s' not found in symbol library '%s'" ),
  931. UnescapeString( symbolName ),
  932. UnescapeString( libName ) );
  933. ercItem->SetErrorMessage( msg );
  934. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  935. }
  936. continue;
  937. }
  938. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  939. constexpr int flags = SCH_ITEM::COMPARE_FLAGS::EQUALITY | SCH_ITEM::COMPARE_FLAGS::ERC;
  940. if( m_settings.IsTestEnabled( ERCE_LIB_SYMBOL_MISMATCH ) )
  941. {
  942. // We have to check for duplicate pins first as they will cause Compare() to fail.
  943. std::vector<wxString> messages;
  944. UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MILS );
  945. CheckDuplicatePins( libSymbolInSchematic, messages, &unitsProvider );
  946. if( !messages.empty() )
  947. {
  948. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DUPLICATE_PIN_ERROR );
  949. ercItem->SetItems( symbol );
  950. msg.Printf( _( "Symbol '%s' has multiple pins with the same pin number" ),
  951. UnescapeString( symbolName ) );
  952. ercItem->SetErrorMessage( msg );
  953. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  954. }
  955. else if( flattenedSymbol->Compare( *libSymbolInSchematic, flags ) != 0 )
  956. {
  957. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_MISMATCH );
  958. ercItem->SetItems( symbol );
  959. msg.Printf( _( "Symbol '%s' doesn't match copy in library '%s'" ),
  960. UnescapeString( symbolName ),
  961. UnescapeString( libName ) );
  962. ercItem->SetErrorMessage( msg );
  963. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  964. }
  965. }
  966. }
  967. for( SCH_MARKER* marker : markers )
  968. {
  969. screen->Append( marker );
  970. err_count += 1;
  971. }
  972. }
  973. return err_count;
  974. }
  975. int ERC_TESTER::TestFootprintLinkIssues( KIFACE* aCvPcb, PROJECT* aProject )
  976. {
  977. wxCHECK( m_schematic, 0 );
  978. wxString msg;
  979. int err_count = 0;
  980. typedef int (*TESTER_FN_PTR)( const wxString&, PROJECT* );
  981. TESTER_FN_PTR linkTester = (TESTER_FN_PTR) aCvPcb->IfaceOrAddress( KIFACE_TEST_FOOTPRINT_LINK );
  982. for( SCH_SHEET_PATH& sheet : m_sheetList )
  983. {
  984. std::vector<SCH_MARKER*> markers;
  985. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  986. {
  987. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  988. wxString footprint = symbol->GetFootprintFieldText( true, &sheet, false );
  989. if( footprint.IsEmpty() )
  990. continue;
  991. LIB_ID fpID;
  992. if( fpID.Parse( footprint, true ) >= 0 )
  993. {
  994. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_FOOTPRINT_LINK_ISSUES );
  995. msg.Printf( _( "'%s' is not a valid footprint identifier." ), footprint );
  996. ercItem->SetErrorMessage( msg );
  997. ercItem->SetItems( symbol );
  998. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  999. continue;
  1000. }
  1001. wxString libName = fpID.GetLibNickname();
  1002. wxString fpName = fpID.GetLibItemName();
  1003. int ret = (linkTester)( footprint, aProject );
  1004. if( ret == KIFACE_TEST_FOOTPRINT_LINK_NO_LIBRARY )
  1005. {
  1006. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_FOOTPRINT_LINK_ISSUES );
  1007. msg.Printf( _( "The current configuration does not include the footprint library '%s'." ),
  1008. libName );
  1009. ercItem->SetErrorMessage( msg );
  1010. ercItem->SetItems( symbol );
  1011. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  1012. }
  1013. else if( ret == KIFACE_TEST_FOOTPRINT_LINK_LIBRARY_NOT_ENABLED )
  1014. {
  1015. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_FOOTPRINT_LINK_ISSUES );
  1016. msg.Printf( _( "The footprint library '%s' is not enabled in the current configuration." ),
  1017. libName );
  1018. ercItem->SetErrorMessage( msg );
  1019. ercItem->SetItems( symbol );
  1020. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  1021. }
  1022. else if( ret == KIFACE_TEST_FOOTPRINT_LINK_NO_FOOTPRINT )
  1023. {
  1024. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_FOOTPRINT_LINK_ISSUES );
  1025. msg.Printf( _( "Footprint '%s' not found in library '%s'." ),
  1026. fpName,
  1027. libName );
  1028. ercItem->SetErrorMessage( msg );
  1029. ercItem->SetItems( symbol );
  1030. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  1031. }
  1032. }
  1033. for( SCH_MARKER* marker : markers )
  1034. {
  1035. sheet.LastScreen()->Append( marker );
  1036. err_count += 1;
  1037. }
  1038. }
  1039. return err_count;
  1040. }
  1041. int ERC_TESTER::TestOffGridEndpoints()
  1042. {
  1043. const int gridSize = m_schematic->Settings().m_ConnectionGridSize;
  1044. int err_count = 0;
  1045. for( SCH_SCREEN* screen = m_screens.GetFirst(); screen; screen = m_screens.GetNext() )
  1046. {
  1047. std::vector<SCH_MARKER*> markers;
  1048. for( SCH_ITEM* item : screen->Items() )
  1049. {
  1050. if( item->Type() == SCH_LINE_T && item->IsConnectable() )
  1051. {
  1052. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  1053. if( ( line->GetStartPoint().x % gridSize ) != 0
  1054. || ( line->GetStartPoint().y % gridSize ) != 0 )
  1055. {
  1056. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_ENDPOINT_OFF_GRID );
  1057. ercItem->SetItems( line );
  1058. markers.emplace_back( new SCH_MARKER( ercItem, line->GetStartPoint() ) );
  1059. }
  1060. else if( ( line->GetEndPoint().x % gridSize ) != 0
  1061. || ( line->GetEndPoint().y % gridSize ) != 0 )
  1062. {
  1063. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_ENDPOINT_OFF_GRID );
  1064. ercItem->SetItems( line );
  1065. markers.emplace_back( new SCH_MARKER( ercItem, line->GetEndPoint() ) );
  1066. }
  1067. }
  1068. else if( item->Type() == SCH_SYMBOL_T )
  1069. {
  1070. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  1071. for( SCH_PIN* pin : symbol->GetPins( nullptr ) )
  1072. {
  1073. VECTOR2I pinPos = pin->GetPosition();
  1074. if( ( pinPos.x % gridSize ) != 0 || ( pinPos.y % gridSize ) != 0 )
  1075. {
  1076. auto ercItem = ERC_ITEM::Create( ERCE_ENDPOINT_OFF_GRID );
  1077. ercItem->SetItems( pin );
  1078. markers.emplace_back( new SCH_MARKER( ercItem, pinPos ) );
  1079. break;
  1080. }
  1081. }
  1082. }
  1083. }
  1084. for( SCH_MARKER* marker : markers )
  1085. {
  1086. screen->Append( marker );
  1087. err_count += 1;
  1088. }
  1089. }
  1090. return err_count;
  1091. }
  1092. int ERC_TESTER::TestSimModelIssues()
  1093. {
  1094. wxString msg;
  1095. WX_STRING_REPORTER reporter( &msg );
  1096. int err_count = 0;
  1097. SIM_LIB_MGR libMgr( &m_schematic->Prj() );
  1098. for( SCH_SHEET_PATH& sheet : m_sheetList )
  1099. {
  1100. if( sheet.GetExcludedFromSim() )
  1101. continue;
  1102. std::vector<SCH_MARKER*> markers;
  1103. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  1104. {
  1105. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  1106. // Power symbols and other symbols which have the reference starting with "#" are
  1107. // not included in simulation
  1108. if( symbol->GetRef( &sheet ).StartsWith( '#' ) || symbol->GetExcludedFromSim() )
  1109. continue;
  1110. // Reset for each symbol
  1111. msg.Clear();
  1112. SIM_LIBRARY::MODEL model = libMgr.CreateModel( &sheet, *symbol, reporter );
  1113. if( !msg.IsEmpty() )
  1114. {
  1115. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SIMULATION_MODEL );
  1116. //Remove \n and \r at e.o.l if any:
  1117. msg.Trim();
  1118. ercItem->SetErrorMessage( msg );
  1119. ercItem->SetItems( symbol );
  1120. markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
  1121. }
  1122. }
  1123. for( SCH_MARKER* marker : markers )
  1124. {
  1125. sheet.LastScreen()->Append( marker );
  1126. err_count += 1;
  1127. }
  1128. }
  1129. return err_count;
  1130. }
  1131. int ERC_TESTER::RunRuleAreaERC()
  1132. {
  1133. int numErrors = 0;
  1134. if( !m_settings.IsTestEnabled( ERCE_OVERLAPPING_RULE_AREAS ) )
  1135. return 0;
  1136. std::map<SCH_SCREEN*, std::vector<SCH_RULE_AREA*>> allScreenRuleAreas;
  1137. for( SCH_SCREEN* screen = m_screens.GetFirst(); screen; screen = m_screens.GetNext() )
  1138. {
  1139. for( SCH_ITEM* item : screen->Items().OfType( SCH_RULE_AREA_T ) )
  1140. {
  1141. allScreenRuleAreas[screen].push_back( static_cast<SCH_RULE_AREA*>( item ) );
  1142. }
  1143. }
  1144. if( m_settings.IsTestEnabled( ERCE_OVERLAPPING_RULE_AREAS ) )
  1145. numErrors += TestRuleAreaOverlappingRuleAreasERC( allScreenRuleAreas );
  1146. return numErrors;
  1147. }
  1148. int ERC_TESTER::TestRuleAreaOverlappingRuleAreasERC(
  1149. std::map<SCH_SCREEN*, std::vector<SCH_RULE_AREA*>>& allScreenRuleAreas )
  1150. {
  1151. int numErrors = 0;
  1152. for( auto screenRuleAreas : allScreenRuleAreas )
  1153. {
  1154. std::vector<SCH_RULE_AREA*>& ruleAreas = screenRuleAreas.second;
  1155. for( std::size_t i = 0; i < ruleAreas.size(); ++i )
  1156. {
  1157. SHAPE_POLY_SET& polyFirst = ruleAreas[i]->GetPolyShape();
  1158. for( std::size_t j = i + 1; j < ruleAreas.size(); ++j )
  1159. {
  1160. SHAPE_POLY_SET polySecond = ruleAreas[j]->GetPolyShape();
  1161. if( polyFirst.Collide( &polySecond ) )
  1162. {
  1163. numErrors++;
  1164. SCH_SCREEN* screen = screenRuleAreas.first;
  1165. SCH_SHEET_PATH firstSheet = screen->GetClientSheetPaths()[0];
  1166. std::shared_ptr<ERC_ITEM> ercItem =
  1167. ERC_ITEM::Create( ERCE_OVERLAPPING_RULE_AREAS );
  1168. ercItem->SetItems( ruleAreas[i], ruleAreas[j] );
  1169. ercItem->SetSheetSpecificPath( firstSheet );
  1170. ercItem->SetItemsSheetPaths( firstSheet, firstSheet );
  1171. SCH_MARKER* marker = new SCH_MARKER( ercItem, ruleAreas[i]->GetPosition() );
  1172. screen->Append( marker );
  1173. }
  1174. }
  1175. }
  1176. }
  1177. return numErrors;
  1178. }
  1179. void ERC_TESTER::RunTests( DS_PROXY_VIEW_ITEM* aDrawingSheet, SCH_EDIT_FRAME* aEditFrame,
  1180. KIFACE* aCvPcb, PROJECT* aProject, PROGRESS_REPORTER* aProgressReporter )
  1181. {
  1182. m_sheetList.AnnotatePowerSymbols();
  1183. // Test duplicate sheet names inside a given sheet. While one can have multiple references
  1184. // to the same file, each must have a unique name.
  1185. if( m_settings.IsTestEnabled( ERCE_DUPLICATE_SHEET_NAME ) )
  1186. {
  1187. if( aProgressReporter )
  1188. aProgressReporter->AdvancePhase( _( "Checking sheet names..." ) );
  1189. TestDuplicateSheetNames( true );
  1190. }
  1191. if( m_settings.IsTestEnabled( ERCE_BUS_ALIAS_CONFLICT ) )
  1192. {
  1193. if( aProgressReporter )
  1194. aProgressReporter->AdvancePhase( _( "Checking bus conflicts..." ) );
  1195. TestConflictingBusAliases();
  1196. }
  1197. // The connection graph has a whole set of ERC checks it can run
  1198. if( aProgressReporter )
  1199. aProgressReporter->AdvancePhase( _( "Checking conflicts..." ) );
  1200. // If we are using the new connectivity, make sure that we do a full-rebuild
  1201. if( aEditFrame )
  1202. {
  1203. if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
  1204. aEditFrame->RecalculateConnections( nullptr, GLOBAL_CLEANUP );
  1205. else
  1206. aEditFrame->RecalculateConnections( nullptr, NO_CLEANUP );
  1207. }
  1208. m_schematic->ConnectionGraph()->RunERC();
  1209. if( aProgressReporter )
  1210. aProgressReporter->AdvancePhase( _( "Checking rule areas..." ) );
  1211. if( m_settings.IsTestEnabled( ERCE_OVERLAPPING_RULE_AREAS ) )
  1212. {
  1213. RunRuleAreaERC();
  1214. }
  1215. if( aProgressReporter )
  1216. aProgressReporter->AdvancePhase( _( "Checking units..." ) );
  1217. // Test is all units of each multiunit symbol have the same footprint assigned.
  1218. if( m_settings.IsTestEnabled( ERCE_DIFFERENT_UNIT_FP ) )
  1219. {
  1220. if( aProgressReporter )
  1221. aProgressReporter->AdvancePhase( _( "Checking footprints..." ) );
  1222. TestMultiunitFootprints();
  1223. }
  1224. if( m_settings.IsTestEnabled( ERCE_MISSING_UNIT )
  1225. || m_settings.IsTestEnabled( ERCE_MISSING_INPUT_PIN )
  1226. || m_settings.IsTestEnabled( ERCE_MISSING_POWER_INPUT_PIN )
  1227. || m_settings.IsTestEnabled( ERCE_MISSING_BIDI_PIN ) )
  1228. {
  1229. TestMissingUnits();
  1230. }
  1231. if( aProgressReporter )
  1232. aProgressReporter->AdvancePhase( _( "Checking pins..." ) );
  1233. if( m_settings.IsTestEnabled( ERCE_DIFFERENT_UNIT_NET ) )
  1234. TestMultUnitPinConflicts();
  1235. // Test pins on each net against the pin connection table
  1236. if( m_settings.IsTestEnabled( ERCE_PIN_TO_PIN_ERROR )
  1237. || m_settings.IsTestEnabled( ERCE_POWERPIN_NOT_DRIVEN )
  1238. || m_settings.IsTestEnabled( ERCE_PIN_NOT_DRIVEN ) )
  1239. {
  1240. TestPinToPin();
  1241. }
  1242. // Test similar labels (i;e. labels which are identical when
  1243. // using case insensitive comparisons)
  1244. if( m_settings.IsTestEnabled( ERCE_SIMILAR_LABELS ) )
  1245. {
  1246. if( aProgressReporter )
  1247. aProgressReporter->AdvancePhase( _( "Checking labels..." ) );
  1248. TestSimilarLabels();
  1249. }
  1250. if( m_settings.IsTestEnabled( ERCE_UNRESOLVED_VARIABLE ) )
  1251. {
  1252. if( aProgressReporter )
  1253. aProgressReporter->AdvancePhase( _( "Checking for unresolved variables..." ) );
  1254. TestTextVars( aDrawingSheet );
  1255. }
  1256. if( m_settings.IsTestEnabled( ERCE_SIMULATION_MODEL ) )
  1257. {
  1258. if( aProgressReporter )
  1259. aProgressReporter->AdvancePhase( _( "Checking SPICE models..." ) );
  1260. TestSimModelIssues();
  1261. }
  1262. if( m_settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
  1263. {
  1264. if( aProgressReporter )
  1265. aProgressReporter->AdvancePhase( _( "Checking no connect pins for connections..." ) );
  1266. TestNoConnectPins();
  1267. }
  1268. if( m_settings.IsTestEnabled( ERCE_LIB_SYMBOL_ISSUES )
  1269. || m_settings.IsTestEnabled( ERCE_LIB_SYMBOL_MISMATCH ) )
  1270. {
  1271. if( aProgressReporter )
  1272. aProgressReporter->AdvancePhase( _( "Checking for library symbol issues..." ) );
  1273. TestLibSymbolIssues();
  1274. }
  1275. if( m_settings.IsTestEnabled( ERCE_FOOTPRINT_LINK_ISSUES ) && aCvPcb )
  1276. {
  1277. if( aProgressReporter )
  1278. aProgressReporter->AdvancePhase( _( "Checking for footprint link issues..." ) );
  1279. TestFootprintLinkIssues( aCvPcb, aProject );
  1280. }
  1281. if( m_settings.IsTestEnabled( ERCE_ENDPOINT_OFF_GRID ) )
  1282. {
  1283. if( aProgressReporter )
  1284. aProgressReporter->AdvancePhase( _( "Checking for off grid pins and wires..." ) );
  1285. TestOffGridEndpoints();
  1286. }
  1287. if( m_settings.IsTestEnabled( ERCE_FOUR_WAY_JUNCTION ) )
  1288. {
  1289. if( aProgressReporter )
  1290. aProgressReporter->AdvancePhase( _( "Checking for four way junctions..." ) );
  1291. TestFourWayJunction();
  1292. }
  1293. if( m_settings.IsTestEnabled( ERCE_UNDEFINED_NETCLASS ) )
  1294. {
  1295. if( aProgressReporter )
  1296. aProgressReporter->AdvancePhase( _( "Checking for undefined netclasses..." ) );
  1297. TestMissingNetclasses();
  1298. }
  1299. m_schematic->ResolveERCExclusionsPostUpdate();
  1300. }