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.

765 lines
26 KiB

18 years ago
18 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-2016 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 1992-2016 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. /**
  26. * @file erc.cpp
  27. * @brief Electrical Rules Check implementation.
  28. */
  29. #include <fctsys.h>
  30. #include <kicad_string.h>
  31. #include <sch_edit_frame.h>
  32. #include <netlist_object.h>
  33. #include <lib_pin.h>
  34. #include <erc.h>
  35. #include <sch_marker.h>
  36. #include <sch_sheet.h>
  37. #include <sch_reference_list.h>
  38. #include <schematic.h>
  39. #include <wx/ffile.h>
  40. #include <ws_draw_item.h>
  41. #include <ws_proxy_view_item.h>
  42. /* ERC tests :
  43. * 1 - conflicts between connected pins ( example: 2 connected outputs )
  44. * 2 - minimal connections requirements ( 1 input *must* be connected to an
  45. * output, or a passive pin )
  46. */
  47. /*
  48. * Minimal ERC requirements:
  49. * All pins *must* be connected (except ELECTRICAL_PINTYPE::PT_NC).
  50. * When a pin is not connected in schematic, the user must place a "non
  51. * connected" symbol to this pin.
  52. * This ensures a forgotten connection will be detected.
  53. */
  54. /* Messages for conflicts :
  55. * ELECTRICAL_PINTYPE::PT_INPUT, ELECTRICAL_PINTYPE::PT_OUTPUT, ELECTRICAL_PINTYPE:PT_:BIDI, ELECTRICAL_PINTYPE::PT_TRISTATE, ELECTRICAL_PINTYPE::PT_PASSIVE,
  56. * ELECTRICAL_PINTYPE::PT_UNSPECIFIED, ELECTRICAL_PINTYPE::PT_POWER_IN, ELECTRICAL_PINTYPE::PT_POWER_OUT, ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR,
  57. * ELECTRICAL_PINTYPE::PT_OPENEMITTER, ELECTRICAL_PINTYPE::PT_NC
  58. * These messages are used to show the ERC matrix in ERC dialog
  59. */
  60. // Messages for matrix rows:
  61. const wxString CommentERC_H[] =
  62. {
  63. _( "Input Pin" ),
  64. _( "Output Pin" ),
  65. _( "Bidirectional Pin" ),
  66. _( "Tri-State Pin" ),
  67. _( "Passive Pin" ),
  68. _( "Unspecified Pin" ),
  69. _( "Power Input Pin" ),
  70. _( "Power Output Pin" ),
  71. _( "Open Collector" ),
  72. _( "Open Emitter" ),
  73. _( "No Connection" )
  74. };
  75. // Messages for matrix columns
  76. const wxString CommentERC_V[] =
  77. {
  78. _( "Input Pin" ),
  79. _( "Output Pin" ),
  80. _( "Bidirectional Pin" ),
  81. _( "Tri-State Pin" ),
  82. _( "Passive Pin" ),
  83. _( "Unspecified Pin" ),
  84. _( "Power Input Pin" ),
  85. _( "Power Output Pin" ),
  86. _( "Open Collector" ),
  87. _( "Open Emitter" ),
  88. _( "No Connection" )
  89. };
  90. int ERC_TESTER::TestDuplicateSheetNames( bool aCreateMarker )
  91. {
  92. SCH_SCREEN* screen;
  93. int err_count = 0;
  94. SCH_SCREENS screenList( m_schematic->Root() );
  95. for( screen = screenList.GetFirst(); screen != nullptr; screen = screenList.GetNext() )
  96. {
  97. std::vector<SCH_SHEET*> list;
  98. for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
  99. list.push_back( static_cast<SCH_SHEET*>( item ) );
  100. for( size_t i = 0; i < list.size(); i++ )
  101. {
  102. SCH_SHEET* sheet = list[i];
  103. for( size_t j = i + 1; j < list.size(); j++ )
  104. {
  105. SCH_SHEET* test_item = list[j];
  106. // We have found a second sheet: compare names
  107. // we are using case insensitive comparison to avoid mistakes between
  108. // similar names like Mysheet and mysheet
  109. if( sheet->GetName().CmpNoCase( test_item->GetName() ) == 0 )
  110. {
  111. if( aCreateMarker )
  112. {
  113. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_DUPLICATE_SHEET_NAME );
  114. ercItem->SetItems( sheet, test_item );
  115. SCH_MARKER* marker = new SCH_MARKER( ercItem, sheet->GetPosition() );
  116. screen->Append( marker );
  117. }
  118. err_count++;
  119. }
  120. }
  121. }
  122. }
  123. return err_count;
  124. }
  125. void ERC_TESTER::TestTextVars( KIGFX::WS_PROXY_VIEW_ITEM* aWorksheet )
  126. {
  127. WS_DRAW_ITEM_LIST wsItems;
  128. if( aWorksheet )
  129. {
  130. wsItems.SetMilsToIUfactor( IU_PER_MILS );
  131. wsItems.BuildWorkSheetGraphicList( aWorksheet->GetPageInfo(), aWorksheet->GetTitleBlock() );
  132. }
  133. SCH_SCREENS screens( m_schematic->Root() );
  134. for( SCH_SCREEN* screen = screens.GetFirst(); screen != NULL; screen = screens.GetNext() )
  135. {
  136. for( SCH_ITEM* item : screen->Items().OfType( SCH_LOCATE_ANY_T ) )
  137. {
  138. if( item->Type() == SCH_COMPONENT_T )
  139. {
  140. SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
  141. for( SCH_FIELD& field : component->GetFields() )
  142. {
  143. if( field.GetShownText().Matches( wxT( "*${*}*" ) ) )
  144. {
  145. wxPoint pos = field.GetPosition() - component->GetPosition();
  146. pos = component->GetTransform().TransformCoordinate( pos );
  147. pos += component->GetPosition();
  148. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  149. ercItem->SetItems( &field );
  150. SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
  151. screen->Append( marker );
  152. }
  153. }
  154. }
  155. else if( item->Type() == SCH_SHEET_T )
  156. {
  157. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  158. for( SCH_FIELD& field : sheet->GetFields() )
  159. {
  160. if( field.GetShownText().Matches( wxT( "*${*}*" ) ) )
  161. {
  162. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  163. ercItem->SetItems( &field );
  164. SCH_MARKER* marker = new SCH_MARKER( ercItem, field.GetPosition() );
  165. screen->Append( marker );
  166. }
  167. }
  168. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  169. {
  170. if( pin->GetShownText().Matches( wxT( "*${*}*" ) ) )
  171. {
  172. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  173. ercItem->SetItems( pin );
  174. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
  175. screen->Append( marker );
  176. }
  177. }
  178. }
  179. else if( SCH_TEXT* text = dynamic_cast<SCH_TEXT*>( item ) )
  180. {
  181. if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
  182. {
  183. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  184. ercItem->SetItems( text );
  185. SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
  186. screen->Append( marker );
  187. }
  188. }
  189. }
  190. for( WS_DRAW_ITEM_BASE* item = wsItems.GetFirst(); item; item = wsItems.GetNext() )
  191. {
  192. if( WS_DRAW_ITEM_TEXT* text = dynamic_cast<WS_DRAW_ITEM_TEXT*>( item ) )
  193. {
  194. if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
  195. {
  196. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
  197. ercItem->SetErrorMessage( _( "Unresolved text variable in worksheet." ) );
  198. SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
  199. screen->Append( marker );
  200. }
  201. }
  202. }
  203. }
  204. }
  205. int ERC_TESTER::TestConflictingBusAliases()
  206. {
  207. wxString msg;
  208. int err_count = 0;
  209. SCH_SCREENS screens( m_schematic->Root() );
  210. std::vector< std::shared_ptr<BUS_ALIAS> > aliases;
  211. for( SCH_SCREEN* screen = screens.GetFirst(); screen != NULL; screen = screens.GetNext() )
  212. {
  213. std::unordered_set< std::shared_ptr<BUS_ALIAS> > screen_aliases = screen->GetBusAliases();
  214. for( const std::shared_ptr<BUS_ALIAS>& alias : screen_aliases )
  215. {
  216. for( const std::shared_ptr<BUS_ALIAS>& test : aliases )
  217. {
  218. if( alias->GetName() == test->GetName() && alias->Members() != test->Members() )
  219. {
  220. msg.Printf( _( "Bus alias %s has conflicting definitions on %s and %s" ),
  221. alias->GetName(),
  222. alias->GetParent()->GetFileName(),
  223. test->GetParent()->GetFileName() );
  224. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_BUS_ALIAS_CONFLICT );
  225. ercItem->SetErrorMessage( msg );
  226. SCH_MARKER* marker = new SCH_MARKER( ercItem, wxPoint() );
  227. test->GetParent()->Append( marker );
  228. ++err_count;
  229. }
  230. }
  231. }
  232. aliases.insert( aliases.end(), screen_aliases.begin(), screen_aliases.end() );
  233. }
  234. return err_count;
  235. }
  236. int ERC_TESTER::TestMultiunitFootprints()
  237. {
  238. SCH_SHEET_LIST sheets = m_schematic->GetSheets();
  239. int errors = 0;
  240. std::map<wxString, LIB_ID> footprints;
  241. SCH_MULTI_UNIT_REFERENCE_MAP refMap;
  242. sheets.GetMultiUnitComponents( refMap, true );
  243. for( auto& component : refMap )
  244. {
  245. auto& refList = component.second;
  246. if( refList.GetCount() == 0 )
  247. {
  248. wxFAIL; // it should not happen
  249. continue;
  250. }
  251. // Reference footprint
  252. SCH_COMPONENT* unit = nullptr;
  253. wxString unitName;
  254. wxString unitFP;
  255. for( unsigned i = 0; i < component.second.GetCount(); ++i )
  256. {
  257. SCH_SHEET_PATH sheetPath = refList.GetItem( i ).GetSheetPath();
  258. unitFP = refList.GetItem( i ).GetComp()->GetField( FOOTPRINT )->GetText();
  259. if( !unitFP.IsEmpty() )
  260. {
  261. unit = refList.GetItem( i ).GetComp();
  262. unitName = unit->GetRef( &sheetPath, true );
  263. break;
  264. }
  265. }
  266. for( unsigned i = 0; i < component.second.GetCount(); ++i )
  267. {
  268. SCH_REFERENCE& secondRef = refList.GetItem( i );
  269. SCH_COMPONENT* secondUnit = secondRef.GetComp();
  270. wxString secondName = secondUnit->GetRef( &secondRef.GetSheetPath(), true );
  271. const wxString secondFp = secondUnit->GetField( FOOTPRINT )->GetText();
  272. wxString msg;
  273. if( !secondFp.IsEmpty() && unitFP != secondFp )
  274. {
  275. msg.Printf( _( "Different footprints assigned to %s and %s" ),
  276. unitName, secondName );
  277. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_DIFFERENT_UNIT_FP );
  278. ercItem->SetErrorMessage( msg );
  279. ercItem->SetItems( unit, secondUnit );
  280. SCH_MARKER* marker = new SCH_MARKER( ercItem, secondUnit->GetPosition() );
  281. secondRef.GetSheetPath().LastScreen()->Append( marker );
  282. ++errors;
  283. }
  284. }
  285. }
  286. return errors;
  287. }
  288. void ERC_TESTER::diagnose( NETLIST_OBJECT* aNetItemRef, NETLIST_OBJECT* aNetItemTst, int aMinConn,
  289. PIN_ERROR aDiag )
  290. {
  291. if( aDiag == PIN_ERROR::OK || aMinConn < 1 || aNetItemRef->m_Type != NETLIST_ITEM::PIN )
  292. return;
  293. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  294. SCH_PIN* pin = static_cast<SCH_PIN*>( aNetItemRef->m_Comp );
  295. if( aNetItemTst == NULL)
  296. {
  297. if( aMinConn == NOD ) /* Nothing driving the net. */
  298. {
  299. if( settings.GetSeverity( ERCE_PIN_NOT_DRIVEN ) != RPT_SEVERITY_IGNORE )
  300. {
  301. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_DRIVEN );
  302. ercItem->SetItems( pin );
  303. SCH_MARKER* marker = new SCH_MARKER( ercItem, aNetItemRef->m_Start );
  304. aNetItemRef->m_SheetPath.LastScreen()->Append( marker );
  305. }
  306. return;
  307. }
  308. }
  309. if( aNetItemTst && aNetItemTst->m_Type == NETLIST_ITEM::PIN ) /* Error between 2 pins */
  310. {
  311. if( settings.GetSeverity( ERCE_PIN_TO_PIN_WARNING ) != RPT_SEVERITY_IGNORE )
  312. {
  313. ERC_ITEM* ercItem = ERC_ITEM::Create(
  314. aDiag == PIN_ERROR::PP_ERROR ? ERCE_PIN_TO_PIN_ERROR : ERCE_PIN_TO_PIN_WARNING );
  315. ercItem->SetItems( pin, static_cast<SCH_PIN*>( aNetItemTst->m_Comp ) );
  316. SCH_MARKER* marker = new SCH_MARKER( ercItem, aNetItemRef->m_Start );
  317. aNetItemRef->m_SheetPath.LastScreen()->Append( marker );
  318. }
  319. }
  320. }
  321. void ERC_TESTER::TestOthersItems( NETLIST_OBJECT_LIST* aList, unsigned aNetItemRef,
  322. unsigned aNetStart, int* aMinConnexion )
  323. {
  324. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  325. unsigned netItemTst = aNetStart;
  326. ELECTRICAL_PINTYPE jj;
  327. PIN_ERROR erc = PIN_ERROR::OK;
  328. /* Analysis of the table of connections. */
  329. ELECTRICAL_PINTYPE ref_elect_type = aList->GetItem( aNetItemRef )->m_ElectricalPinType;
  330. int local_minconn = NOC;
  331. if( ref_elect_type == ELECTRICAL_PINTYPE::PT_NC )
  332. local_minconn = NPI;
  333. /* Test pins connected to NetItemRef */
  334. for( ; ; netItemTst++ )
  335. {
  336. if( aNetItemRef == netItemTst )
  337. continue;
  338. // We examine only a given net. We stop the search if the net changes
  339. if( ( netItemTst >= aList->size() ) // End of list
  340. || ( aList->GetItemNet( aNetItemRef ) !=
  341. aList->GetItemNet( netItemTst ) ) ) // End of net
  342. {
  343. /* End net code found: minimum connection test. */
  344. if( ( *aMinConnexion < NET_NC ) && ( local_minconn < NET_NC ) )
  345. {
  346. /* Not connected or not driven pin. */
  347. bool seterr = true;
  348. if( local_minconn == NOC && aList->GetItemType( aNetItemRef ) == NETLIST_ITEM::PIN )
  349. {
  350. /* This pin is not connected: for multiple part per
  351. * package, and duplicated pin,
  352. * search for another instance of this pin
  353. * this will be flagged only if all instances of this pin
  354. * are not connected
  355. * TODO test also if instances connected are connected to
  356. * the same net
  357. */
  358. for( unsigned duplicate = 0; duplicate < aList->size(); duplicate++ )
  359. {
  360. if( aList->GetItemType( duplicate ) != NETLIST_ITEM::PIN )
  361. continue;
  362. if( duplicate == aNetItemRef )
  363. continue;
  364. if( aList->GetItem( aNetItemRef )->m_PinNum !=
  365. aList->GetItem( duplicate )->m_PinNum )
  366. continue;
  367. if( ( (SCH_COMPONENT*) aList->GetItem( aNetItemRef )->
  368. m_Link )->GetRef( &aList->GetItem( aNetItemRef )-> m_SheetPath ) !=
  369. ( (SCH_COMPONENT*) aList->GetItem( duplicate )->m_Link )
  370. ->GetRef( &aList->GetItem( duplicate )->m_SheetPath ) )
  371. continue;
  372. // Same component and same pin. Do dot create error for this pin
  373. // if the other pin is connected (i.e. if duplicate net has another
  374. // item)
  375. if( (duplicate > 0)
  376. && ( aList->GetItemNet( duplicate ) ==
  377. aList->GetItemNet( duplicate - 1 ) ) )
  378. seterr = false;
  379. if( (duplicate < aList->size() - 1)
  380. && ( aList->GetItemNet( duplicate ) ==
  381. aList->GetItemNet( duplicate + 1 ) ) )
  382. seterr = false;
  383. }
  384. }
  385. if( seterr )
  386. {
  387. diagnose( aList->GetItem( aNetItemRef ), nullptr, local_minconn,
  388. PIN_ERROR::WARNING );
  389. }
  390. *aMinConnexion = DRV; // inhibiting other messages of this
  391. // type for the net.
  392. }
  393. return;
  394. }
  395. switch( aList->GetItemType( netItemTst ) )
  396. {
  397. case NETLIST_ITEM::ITEM_UNSPECIFIED:
  398. case NETLIST_ITEM::SEGMENT:
  399. case NETLIST_ITEM::BUS:
  400. case NETLIST_ITEM::JUNCTION:
  401. case NETLIST_ITEM::LABEL:
  402. case NETLIST_ITEM::HIERLABEL:
  403. case NETLIST_ITEM::BUSLABELMEMBER:
  404. case NETLIST_ITEM::HIERBUSLABELMEMBER:
  405. case NETLIST_ITEM::SHEETBUSLABELMEMBER:
  406. case NETLIST_ITEM::SHEETLABEL:
  407. case NETLIST_ITEM::GLOBLABEL:
  408. case NETLIST_ITEM::GLOBBUSLABELMEMBER:
  409. case NETLIST_ITEM::PINLABEL:
  410. break;
  411. case NETLIST_ITEM::NOCONNECT:
  412. local_minconn = std::max( NET_NC, local_minconn );
  413. break;
  414. case NETLIST_ITEM::PIN:
  415. jj = aList->GetItem( netItemTst )->m_ElectricalPinType;
  416. local_minconn = std::max( settings.GetPinMinDrive( ref_elect_type, jj ),
  417. local_minconn );
  418. if( netItemTst <= aNetItemRef )
  419. break;
  420. if( erc == PIN_ERROR::OK )
  421. {
  422. erc = settings.GetPinMapValue( ref_elect_type, jj );
  423. if( erc != PIN_ERROR::OK )
  424. {
  425. if( aList->GetConnectionType( netItemTst ) == NET_CONNECTION::UNCONNECTED )
  426. {
  427. aList->SetConnectionType( netItemTst,
  428. NET_CONNECTION::NOCONNECT_SYMBOL_PRESENT );
  429. }
  430. diagnose( aList->GetItem( aNetItemRef ), aList->GetItem( netItemTst ), 1, erc );
  431. }
  432. }
  433. break;
  434. }
  435. }
  436. }
  437. int ERC_TESTER::TestNoConnectPins()
  438. {
  439. int err_count = 0;
  440. for( const SCH_SHEET_PATH& sheet : m_schematic->GetSheets() )
  441. {
  442. std::map<wxPoint, std::vector<SCH_PIN*>> pinMap;
  443. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_COMPONENT_T ) )
  444. {
  445. SCH_COMPONENT* comp = static_cast<SCH_COMPONENT*>( item );
  446. for( SCH_PIN* pin : comp->GetSchPins( &sheet ) )
  447. {
  448. if( pin->GetLibPin()->GetType() == ELECTRICAL_PINTYPE::PT_NC )
  449. pinMap[pin->GetPosition()].emplace_back( pin );
  450. }
  451. }
  452. for( auto& pair : pinMap )
  453. {
  454. if( pair.second.size() > 1 )
  455. {
  456. err_count++;
  457. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
  458. ercItem->SetItems( pair.second[0], pair.second[1],
  459. pair.second.size() > 2 ? pair.second[2] : nullptr,
  460. pair.second.size() > 3 ? pair.second[3] : nullptr );
  461. ercItem->SetErrorMessage( _( "Pins with \"no connection\" type are connected" ) );
  462. SCH_MARKER* marker = new SCH_MARKER( ercItem, pair.first );
  463. sheet.LastScreen()->Append( marker );
  464. }
  465. }
  466. }
  467. return err_count;
  468. }
  469. // this code try to detect similar labels, i.e. labels which are identical
  470. // when they are compared using case insensitive coparisons.
  471. // A helper struct to compare NETLIST_OBJECT items by sheetpath and label texts
  472. // for a std::set<NETLIST_OBJECT*> container
  473. // the full text is "sheetpath+label" for local labels and "label" for global labels
  474. struct compare_labels
  475. {
  476. bool operator() ( const NETLIST_OBJECT* lab1, const NETLIST_OBJECT* lab2 ) const
  477. {
  478. wxString str1 = lab1->m_SheetPath.PathAsString() + lab1->m_Label;
  479. wxString str2 = lab2->m_SheetPath.PathAsString() + lab2->m_Label;
  480. return str1.Cmp( str2 ) < 0;
  481. }
  482. };
  483. struct compare_label_names
  484. {
  485. bool operator() ( const NETLIST_OBJECT* lab1, const NETLIST_OBJECT* lab2 ) const
  486. {
  487. return lab1->m_Label.Cmp( lab2->m_Label ) < 0;
  488. }
  489. };
  490. struct compare_paths
  491. {
  492. bool operator() ( const NETLIST_OBJECT* lab1, const NETLIST_OBJECT* lab2 ) const
  493. {
  494. return lab1->m_SheetPath.Path() < lab2->m_SheetPath.Path();
  495. }
  496. };
  497. // Helper functions to build the warning messages about Similar Labels:
  498. static int countIndenticalLabels( std::vector<NETLIST_OBJECT*>& aList, NETLIST_OBJECT* aRef );
  499. static void SimilarLabelsDiagnose( NETLIST_OBJECT* aItemA, NETLIST_OBJECT* aItemB );
  500. void NETLIST_OBJECT_LIST::TestforSimilarLabels()
  501. {
  502. // Similar labels which are different when using case sensitive comparisons
  503. // but are equal when using case insensitive comparisons
  504. // list of all labels (used the better item to build diag messages)
  505. std::vector<NETLIST_OBJECT*> fullLabelList;
  506. // list of all labels , each label appears only once (used to to detect similar labels)
  507. std::set<NETLIST_OBJECT*, compare_labels> uniqueLabelList;
  508. wxString msg;
  509. // Build a list of differents labels. If inside a given sheet there are
  510. // more than one given label, only one label is stored.
  511. // not also the sheet labels are not taken in account for 2 reasons:
  512. // * they are in the root sheet but they are seen only from the child sheet
  513. // * any mismatch between child sheet hierarchical labels and the sheet label
  514. // already detected by ERC
  515. for( unsigned netItem = 0; netItem < size(); ++netItem )
  516. {
  517. switch( GetItemType( netItem ) )
  518. {
  519. case NETLIST_ITEM::LABEL:
  520. case NETLIST_ITEM::BUSLABELMEMBER:
  521. case NETLIST_ITEM::PINLABEL:
  522. case NETLIST_ITEM::GLOBBUSLABELMEMBER:
  523. case NETLIST_ITEM::HIERLABEL:
  524. case NETLIST_ITEM::HIERBUSLABELMEMBER:
  525. case NETLIST_ITEM::GLOBLABEL:
  526. // add this label in lists
  527. uniqueLabelList.insert( GetItem( netItem ) );
  528. fullLabelList.push_back( GetItem( netItem ) );
  529. break;
  530. case NETLIST_ITEM::SHEETLABEL:
  531. case NETLIST_ITEM::SHEETBUSLABELMEMBER:
  532. default:
  533. break;
  534. }
  535. }
  536. // build global labels and compare
  537. std::set<NETLIST_OBJECT*, compare_label_names> loc_labelList;
  538. for( NETLIST_OBJECT* label : uniqueLabelList )
  539. {
  540. if( label->IsLabelGlobal() )
  541. loc_labelList.insert( label );
  542. }
  543. // compare global labels (same label names appears only once in list)
  544. for( auto it = loc_labelList.begin(); it != loc_labelList.end(); ++it )
  545. {
  546. auto it_aux = it;
  547. for( ++it_aux; it_aux != loc_labelList.end(); ++it_aux )
  548. {
  549. if( (*it)->m_Label.CmpNoCase( (*it_aux)->m_Label ) == 0 )
  550. {
  551. // Create new marker for ERC.
  552. int cntA = countIndenticalLabels( fullLabelList, *it );
  553. int cntB = countIndenticalLabels( fullLabelList, *it_aux );
  554. if( cntA <= cntB )
  555. SimilarLabelsDiagnose( (*it), (*it_aux) );
  556. else
  557. SimilarLabelsDiagnose( (*it_aux), (*it) );
  558. }
  559. }
  560. }
  561. // Build paths list
  562. std::set<NETLIST_OBJECT*, compare_paths> pathsList;
  563. for( NETLIST_OBJECT* label : uniqueLabelList )
  564. pathsList.insert( label );
  565. // Examine each label inside a sheet path:
  566. for( NETLIST_OBJECT* candidate : pathsList )
  567. {
  568. loc_labelList.clear();
  569. for( NETLIST_OBJECT* uniqueLabel : uniqueLabelList)
  570. {
  571. if( candidate->m_SheetPath.Path() == uniqueLabel->m_SheetPath.Path() )
  572. loc_labelList.insert( uniqueLabel );
  573. }
  574. // at this point, loc_labelList contains labels of the current sheet path.
  575. // Detect similar labels (same label names appears only once in list)
  576. for( auto ref_it = loc_labelList.begin(); ref_it != loc_labelList.end(); ++ref_it )
  577. {
  578. NETLIST_OBJECT* ref_item = *ref_it;
  579. auto it_aux = ref_it;
  580. for( ++it_aux; it_aux != loc_labelList.end(); ++it_aux )
  581. {
  582. // global label versus global label was already examined.
  583. // here, at least one label must be local
  584. if( ref_item->IsLabelGlobal() && ( *it_aux )->IsLabelGlobal() )
  585. continue;
  586. if( ref_item->m_Label.CmpNoCase( ( *it_aux )->m_Label ) == 0 )
  587. {
  588. // Create new marker for ERC.
  589. int cntA = countIndenticalLabels( fullLabelList, ref_item );
  590. int cntB = countIndenticalLabels( fullLabelList, *it_aux );
  591. if( cntA <= cntB )
  592. SimilarLabelsDiagnose( ref_item, ( *it_aux ) );
  593. else
  594. SimilarLabelsDiagnose( ( *it_aux ), ref_item );
  595. }
  596. }
  597. }
  598. }
  599. }
  600. // Helper function: count the number of labels identical to aLabel
  601. // for global label: global labels in the full project
  602. // for local label: all labels in the current sheet
  603. static int countIndenticalLabels( std::vector<NETLIST_OBJECT*>& aList, NETLIST_OBJECT* aRef )
  604. {
  605. int count = 0;
  606. if( aRef->IsLabelGlobal() )
  607. {
  608. for( NETLIST_OBJECT* i : aList )
  609. {
  610. if( i->IsLabelGlobal() && i->m_Label == aRef->m_Label )
  611. count++;
  612. }
  613. }
  614. else
  615. {
  616. for( NETLIST_OBJECT* i : aList )
  617. {
  618. if( i->m_Label == aRef->m_Label && i->m_SheetPath.Path() == aRef->m_SheetPath.Path() )
  619. count++;
  620. }
  621. }
  622. return count;
  623. }
  624. // Helper function: creates a marker for similar labels ERC warning
  625. static void SimilarLabelsDiagnose( NETLIST_OBJECT* aItemA, NETLIST_OBJECT* aItemB )
  626. {
  627. ERC_ITEM* ercItem = ERC_ITEM::Create( ERCE_SIMILAR_LABELS );
  628. ercItem->SetItems( aItemA->m_Comp, aItemB->m_Comp );
  629. SCH_MARKER* marker = new SCH_MARKER( ercItem, aItemA->m_Start );
  630. aItemA->m_SheetPath.LastScreen()->Append( marker );
  631. }