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.

2910 lines
97 KiB

6 years ago
5 years ago
6 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 CERN
  5. * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Jon Evans <jon@craftyjon.com>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License along
  20. * with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #include <list>
  23. #include <thread>
  24. #include <future>
  25. #include <vector>
  26. #include <unordered_map>
  27. #include <profile.h>
  28. #include <common.h>
  29. #include <core/kicad_algo.h>
  30. #include <erc.h>
  31. #include <pin_type.h>
  32. #include <sch_bus_entry.h>
  33. #include <sch_symbol.h>
  34. #include <sch_edit_frame.h>
  35. #include <sch_line.h>
  36. #include <sch_marker.h>
  37. #include <sch_pin.h>
  38. #include <sch_sheet.h>
  39. #include <sch_sheet_path.h>
  40. #include <sch_sheet_pin.h>
  41. #include <sch_text.h>
  42. #include <schematic.h>
  43. #include <connection_graph.h>
  44. #include <widgets/ui_common.h>
  45. #include <string_utils.h>
  46. #include <wx/log.h>
  47. #include <advanced_config.h> // for realtime connectivity switch
  48. /*
  49. * Flag to enable connectivity profiling
  50. * @ingroup trace_env_vars
  51. */
  52. static const wxChar ConnProfileMask[] = wxT( "CONN_PROFILE" );
  53. /*
  54. * Flag to enable connectivity tracing
  55. * @ingroup trace_env_vars
  56. */
  57. static const wxChar ConnTrace[] = wxT( "CONN" );
  58. bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers )
  59. {
  60. PRIORITY highest_priority = PRIORITY::INVALID;
  61. std::vector<SCH_ITEM*> candidates;
  62. std::vector<SCH_ITEM*> strong_drivers;
  63. m_driver = nullptr;
  64. // Hierarchical labels are lower priority than local labels here,
  65. // because on the first pass we want local labels to drive subgraphs
  66. // so that we can identify same-sheet neighbors and link them together.
  67. // Hierarchical labels will end up overriding the final net name if
  68. // a higher-level sheet has a different name during the hierarchical
  69. // pass.
  70. for( SCH_ITEM* item : m_drivers )
  71. {
  72. PRIORITY item_priority = GetDriverPriority( item );
  73. if( item_priority == PRIORITY::PIN
  74. && !static_cast<SCH_PIN*>( item )->GetParentSymbol()->IsInNetlist() )
  75. continue;
  76. if( item_priority >= PRIORITY::HIER_LABEL )
  77. strong_drivers.push_back( item );
  78. if( item_priority > highest_priority )
  79. {
  80. candidates.clear();
  81. candidates.push_back( item );
  82. highest_priority = item_priority;
  83. }
  84. else if( !candidates.empty() && ( item_priority == highest_priority ) )
  85. {
  86. candidates.push_back( item );
  87. }
  88. }
  89. if( highest_priority >= PRIORITY::HIER_LABEL )
  90. m_strong_driver = true;
  91. // Power pins are 5, global labels are 6
  92. m_local_driver = ( highest_priority < PRIORITY::POWER_PIN );
  93. if( !candidates.empty() )
  94. {
  95. if( candidates.size() > 1 )
  96. {
  97. if( highest_priority == PRIORITY::SHEET_PIN )
  98. {
  99. // We have multiple options, and they are all hierarchical
  100. // sheet pins. Let's prefer outputs over inputs.
  101. for( SCH_ITEM* c : candidates )
  102. {
  103. SCH_SHEET_PIN* p = static_cast<SCH_SHEET_PIN*>( c );
  104. if( p->GetShape() == PINSHEETLABEL_SHAPE::PS_OUTPUT )
  105. {
  106. m_driver = c;
  107. break;
  108. }
  109. }
  110. }
  111. else
  112. {
  113. // See if a previous driver is still a candidate
  114. void* previousDriver = nullptr;
  115. for( SCH_ITEM* member : m_items )
  116. {
  117. if( SCH_CONNECTION* mc = member->Connection( &m_sheet ) )
  118. {
  119. if( mc->GetLastDriver() )
  120. {
  121. previousDriver = mc->GetLastDriver();
  122. break;
  123. }
  124. }
  125. }
  126. // For all other driver types, sort by name
  127. std::sort( candidates.begin(), candidates.end(),
  128. [&]( SCH_ITEM* a, SCH_ITEM* b ) -> bool
  129. {
  130. // meet irreflexive requirements of std::sort
  131. if( a == b )
  132. return false;
  133. SCH_CONNECTION* ac = a->Connection( &m_sheet );
  134. SCH_CONNECTION* bc = b->Connection( &m_sheet );
  135. // Ensure we don't pick the subset over the superset
  136. if( ac->IsBus() && bc->IsBus() )
  137. return bc->IsSubsetOf( ac );
  138. if( a == previousDriver )
  139. return true;
  140. else if( b == previousDriver )
  141. return false;
  142. else
  143. return GetNameForDriver( a ) < GetNameForDriver( b );
  144. } );
  145. }
  146. }
  147. if( !m_driver )
  148. m_driver = candidates[0];
  149. }
  150. if( strong_drivers.size() > 1 )
  151. m_multiple_drivers = true;
  152. // Drop weak drivers
  153. if( m_strong_driver )
  154. m_drivers = strong_drivers;
  155. // Cache driver connection
  156. if( m_driver )
  157. {
  158. m_driver_connection = m_driver->Connection( &m_sheet );
  159. m_driver_connection->ConfigureFromLabel( GetNameForDriver( m_driver ) );
  160. m_driver_connection->SetDriver( m_driver );
  161. m_driver_connection->ClearDirty();
  162. }
  163. else
  164. {
  165. m_driver_connection = nullptr;
  166. }
  167. if( aCheckMultipleDrivers && m_multiple_drivers )
  168. {
  169. // First check if all the candidates are actually the same
  170. bool same = true;
  171. wxString first = GetNameForDriver( candidates[0] );
  172. SCH_ITEM* second_item = nullptr;
  173. for( unsigned i = 1; i < candidates.size(); i++ )
  174. {
  175. if( GetNameForDriver( candidates[i] ) != first )
  176. {
  177. second_item = candidates[i];
  178. same = false;
  179. break;
  180. }
  181. }
  182. if( !same )
  183. {
  184. m_first_driver = m_driver;
  185. m_second_driver = second_item;
  186. }
  187. }
  188. return ( m_driver != nullptr );
  189. }
  190. wxString CONNECTION_SUBGRAPH::GetNetName() const
  191. {
  192. if( !m_driver || m_dirty )
  193. return "";
  194. if( !m_driver->Connection( &m_sheet ) )
  195. {
  196. #ifdef CONNECTIVITY_DEBUG
  197. wxASSERT_MSG( false, "Tried to get the net name of an item with no connection" );
  198. #endif
  199. return "";
  200. }
  201. return m_driver->Connection( &m_sheet )->Name();
  202. }
  203. std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetBusLabels() const
  204. {
  205. std::vector<SCH_ITEM*> labels;
  206. for( SCH_ITEM* item : m_drivers )
  207. {
  208. switch( item->Type() )
  209. {
  210. case SCH_LABEL_T:
  211. case SCH_GLOBAL_LABEL_T:
  212. {
  213. SCH_CONNECTION* label_conn = item->Connection( &m_sheet );
  214. // Only consider bus vectors
  215. if( label_conn->Type() == CONNECTION_TYPE::BUS )
  216. labels.push_back( item );
  217. break;
  218. }
  219. default: break;
  220. }
  221. }
  222. return labels;
  223. }
  224. wxString CONNECTION_SUBGRAPH::driverName( SCH_ITEM* aItem ) const
  225. {
  226. switch( aItem->Type() )
  227. {
  228. case SCH_PIN_T:
  229. {
  230. bool forceNoConnect = m_no_connect != nullptr;
  231. SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
  232. return pin->GetDefaultNetName( m_sheet, forceNoConnect );
  233. break;
  234. }
  235. case SCH_LABEL_T:
  236. case SCH_GLOBAL_LABEL_T:
  237. case SCH_HIER_LABEL_T:
  238. case SCH_SHEET_PIN_T:
  239. {
  240. return EscapeString( static_cast<SCH_TEXT*>( aItem )->GetShownText(), CTX_NETNAME );
  241. break;
  242. }
  243. default:
  244. wxFAIL_MSG( "Unhandled item type in GetNameForDriver" );
  245. break;
  246. }
  247. return wxEmptyString;
  248. }
  249. const wxString& CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem )
  250. {
  251. auto it = m_driver_name_cache.find( aItem );
  252. if( it != m_driver_name_cache.end() )
  253. return it->second;
  254. m_driver_name_cache[aItem] = driverName( aItem );
  255. return m_driver_name_cache.at( aItem );
  256. }
  257. const wxString CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem ) const
  258. {
  259. auto it = m_driver_name_cache.find( aItem );
  260. if( it != m_driver_name_cache.end() )
  261. return it->second;
  262. return driverName( aItem );
  263. }
  264. void CONNECTION_SUBGRAPH::Absorb( CONNECTION_SUBGRAPH* aOther )
  265. {
  266. wxASSERT( m_sheet == aOther->m_sheet );
  267. for( SCH_ITEM* item : aOther->m_items )
  268. {
  269. item->Connection( &m_sheet )->SetSubgraphCode( m_code );
  270. AddItem( item );
  271. }
  272. m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
  273. m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
  274. m_multiple_drivers |= aOther->m_multiple_drivers;
  275. aOther->m_absorbed = true;
  276. aOther->m_dirty = false;
  277. aOther->m_driver = nullptr;
  278. aOther->m_driver_connection = nullptr;
  279. aOther->m_absorbed_by = this;
  280. }
  281. void CONNECTION_SUBGRAPH::AddItem( SCH_ITEM* aItem )
  282. {
  283. m_items.push_back( aItem );
  284. if( aItem->Connection( &m_sheet )->IsDriver() )
  285. m_drivers.push_back( aItem );
  286. if( aItem->Type() == SCH_SHEET_PIN_T )
  287. m_hier_pins.push_back( static_cast<SCH_SHEET_PIN*>( aItem ) );
  288. else if( aItem->Type() == SCH_HIER_LABEL_T )
  289. m_hier_ports.push_back( static_cast<SCH_HIERLABEL*>( aItem ) );
  290. }
  291. void CONNECTION_SUBGRAPH::UpdateItemConnections()
  292. {
  293. if( !m_driver_connection )
  294. return;
  295. for( SCH_ITEM* item : m_items )
  296. {
  297. SCH_CONNECTION* item_conn = item->Connection( &m_sheet );
  298. if( !item_conn )
  299. item_conn = item->InitializeConnection( m_sheet, m_graph );
  300. if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
  301. ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
  302. {
  303. continue;
  304. }
  305. if( item != m_driver )
  306. {
  307. item_conn->Clone( *m_driver_connection );
  308. item_conn->ClearDirty();
  309. }
  310. }
  311. }
  312. CONNECTION_SUBGRAPH::PRIORITY CONNECTION_SUBGRAPH::GetDriverPriority( SCH_ITEM* aDriver )
  313. {
  314. if( !aDriver )
  315. return PRIORITY::NONE;
  316. switch( aDriver->Type() )
  317. {
  318. case SCH_SHEET_PIN_T: return PRIORITY::SHEET_PIN;
  319. case SCH_HIER_LABEL_T: return PRIORITY::HIER_LABEL;
  320. case SCH_LABEL_T: return PRIORITY::LOCAL_LABEL;
  321. case SCH_GLOBAL_LABEL_T: return PRIORITY::GLOBAL;
  322. case SCH_PIN_T:
  323. {
  324. auto sch_pin = static_cast<SCH_PIN*>( aDriver );
  325. if( sch_pin->IsPowerConnection() )
  326. return PRIORITY::POWER_PIN;
  327. else
  328. return PRIORITY::PIN;
  329. }
  330. default: return PRIORITY::NONE;
  331. }
  332. }
  333. bool CONNECTION_GRAPH::m_allowRealTime = true;
  334. void CONNECTION_GRAPH::Reset()
  335. {
  336. for( auto& subgraph : m_subgraphs )
  337. delete subgraph;
  338. m_items.clear();
  339. m_subgraphs.clear();
  340. m_driver_subgraphs.clear();
  341. m_sheet_to_subgraphs_map.clear();
  342. m_invisible_power_pins.clear();
  343. m_bus_alias_cache.clear();
  344. m_net_name_to_code_map.clear();
  345. m_bus_name_to_code_map.clear();
  346. m_net_code_to_subgraphs_map.clear();
  347. m_net_name_to_subgraphs_map.clear();
  348. m_item_to_subgraph_map.clear();
  349. m_local_label_cache.clear();
  350. m_global_label_cache.clear();
  351. m_last_net_code = 1;
  352. m_last_bus_code = 1;
  353. m_last_subgraph_code = 1;
  354. }
  355. void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional,
  356. std::function<void( SCH_ITEM* )>* aChangedItemHandler )
  357. {
  358. PROF_COUNTER recalc_time( "CONNECTION_GRAPH::Recalculate" );
  359. if( aUnconditional )
  360. Reset();
  361. PROF_COUNTER update_items( "updateItemConnectivity" );
  362. m_sheetList = aSheetList;
  363. for( const SCH_SHEET_PATH& sheet : aSheetList )
  364. {
  365. std::vector<SCH_ITEM*> items;
  366. for( SCH_ITEM* item : sheet.LastScreen()->Items() )
  367. {
  368. if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
  369. items.push_back( item );
  370. }
  371. m_items.reserve( m_items.size() + items.size() );
  372. updateItemConnectivity( sheet, items );
  373. // UpdateDanglingState() also adds connected items for SCH_TEXT
  374. sheet.LastScreen()->TestDanglingEnds( &sheet, aChangedItemHandler );
  375. }
  376. if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
  377. update_items.Show();
  378. PROF_COUNTER build_graph( "buildConnectionGraph" );
  379. buildConnectionGraph();
  380. if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
  381. build_graph.Show();
  382. recalc_time.Stop();
  383. if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
  384. recalc_time.Show();
  385. #ifndef DEBUG
  386. // Pressure relief valve for release builds
  387. const double max_recalc_time_msecs = 250.;
  388. if( m_allowRealTime && ADVANCED_CFG::GetCfg().m_RealTimeConnectivity &&
  389. recalc_time.msecs() > max_recalc_time_msecs )
  390. {
  391. m_allowRealTime = false;
  392. }
  393. #endif
  394. }
  395. void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
  396. const std::vector<SCH_ITEM*>& aItemList )
  397. {
  398. std::map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
  399. for( SCH_ITEM* item : aItemList )
  400. {
  401. std::vector< wxPoint > points = item->GetConnectionPoints();
  402. item->ConnectedItems( aSheet ).clear();
  403. if( item->Type() == SCH_SHEET_T )
  404. {
  405. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  406. {
  407. pin->InitializeConnection( aSheet, this );
  408. pin->ConnectedItems( aSheet ).clear();
  409. connection_map[ pin->GetTextPos() ].push_back( pin );
  410. m_items.emplace_back( pin );
  411. }
  412. }
  413. else if( item->Type() == SCH_SYMBOL_T )
  414. {
  415. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  416. for( SCH_PIN* pin : symbol->GetPins( &aSheet ) )
  417. {
  418. pin->InitializeConnection( aSheet, this );
  419. wxPoint pos = pin->GetPosition();
  420. // because calling the first time is not thread-safe
  421. pin->GetDefaultNetName( aSheet );
  422. pin->ConnectedItems( aSheet ).clear();
  423. // Invisible power pins need to be post-processed later
  424. if( pin->IsPowerConnection() && !pin->IsVisible() )
  425. m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
  426. connection_map[ pos ].push_back( pin );
  427. m_items.emplace_back( pin );
  428. }
  429. }
  430. else
  431. {
  432. m_items.emplace_back( item );
  433. auto conn = item->InitializeConnection( aSheet, this );
  434. // Set bus/net property here so that the propagation code uses it
  435. switch( item->Type() )
  436. {
  437. case SCH_LINE_T:
  438. conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
  439. CONNECTION_TYPE::NET );
  440. break;
  441. case SCH_BUS_BUS_ENTRY_T:
  442. conn->SetType( CONNECTION_TYPE::BUS );
  443. // clean previous (old) links:
  444. static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
  445. static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
  446. break;
  447. case SCH_PIN_T:
  448. conn->SetType( CONNECTION_TYPE::NET );
  449. break;
  450. case SCH_BUS_WIRE_ENTRY_T:
  451. conn->SetType( CONNECTION_TYPE::NET );
  452. // clean previous (old) link:
  453. static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
  454. break;
  455. default:
  456. break;
  457. }
  458. for( const wxPoint& point : points )
  459. connection_map[ point ].push_back( item );
  460. }
  461. item->SetConnectivityDirty( false );
  462. }
  463. for( const auto& it : connection_map )
  464. {
  465. auto connection_vec = it.second;
  466. for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
  467. {
  468. SCH_ITEM* connected_item = *primary_it;
  469. // Bus entries are special: they can have connection points in the
  470. // middle of a wire segment, because the junction algo doesn't split
  471. // the segment in two where you place a bus entry. This means that
  472. // bus entries that don't land on the end of a line segment need to
  473. // have "virtual" connection points to the segments they graphically
  474. // touch.
  475. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  476. {
  477. // If this location only has the connection point of the bus
  478. // entry itself, this means that either the bus entry is not
  479. // connected to anything graphically, or that it is connected to
  480. // a segment at some point other than at one of the endpoints.
  481. if( connection_vec.size() == 1 )
  482. {
  483. SCH_SCREEN* screen = aSheet.LastScreen();
  484. SCH_LINE* bus = screen->GetBus( it.first );
  485. if( bus )
  486. {
  487. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  488. bus_entry->m_connected_bus_item = bus;
  489. }
  490. }
  491. }
  492. // Bus-to-bus entries are treated just like bus wires
  493. else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
  494. {
  495. if( connection_vec.size() < 2 )
  496. {
  497. SCH_SCREEN* screen = aSheet.LastScreen();
  498. SCH_LINE* bus = screen->GetBus( it.first );
  499. if( bus )
  500. {
  501. auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
  502. if( it.first == bus_entry->GetPosition() )
  503. bus_entry->m_connected_bus_items[0] = bus;
  504. else
  505. bus_entry->m_connected_bus_items[1] = bus;
  506. bus_entry->ConnectedItems( aSheet ).insert( bus );
  507. bus->ConnectedItems( aSheet ).insert( bus_entry );
  508. }
  509. }
  510. }
  511. // Change junctions to be on bus junction layer if they are touching a bus
  512. else if( connected_item->Type() == SCH_JUNCTION_T )
  513. {
  514. SCH_SCREEN* screen = aSheet.LastScreen();
  515. SCH_LINE* bus = screen->GetBus( it.first );
  516. connected_item->SetLayer( bus ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
  517. }
  518. for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
  519. {
  520. auto test_item = *test_it;
  521. if( connected_item != test_item &&
  522. connected_item->ConnectionPropagatesTo( test_item ) &&
  523. test_item->ConnectionPropagatesTo( connected_item ) )
  524. {
  525. connected_item->ConnectedItems( aSheet ).insert( test_item );
  526. test_item->ConnectedItems( aSheet ).insert( connected_item );
  527. }
  528. // Set up the link between the bus entry net and the bus
  529. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  530. {
  531. if( test_item->Connection( &aSheet )->IsBus() )
  532. {
  533. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  534. bus_entry->m_connected_bus_item = test_item;
  535. }
  536. }
  537. }
  538. // If we got this far and did not find a connected bus item for a bus entry,
  539. // we should do a manual scan in case there is a bus item on this connection
  540. // point but we didn't pick it up earlier because there is *also* a net item here.
  541. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  542. {
  543. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  544. if( !bus_entry->m_connected_bus_item )
  545. {
  546. auto screen = aSheet.LastScreen();
  547. auto bus = screen->GetBus( it.first );
  548. if( bus )
  549. bus_entry->m_connected_bus_item = bus;
  550. }
  551. }
  552. }
  553. }
  554. }
  555. // TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
  556. // to the same subgraph necessarily if it runs over and over again on the same
  557. // sheet. We need:
  558. //
  559. // a) a cache of net/bus codes, like used before
  560. // b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
  561. // c) some way of trying to avoid changing net names. so we should keep track
  562. // of the previous driver of a net, and if it comes down to choosing between
  563. // equally-prioritized drivers, choose the one that already exists as a driver
  564. // on some portion of the items.
  565. void CONNECTION_GRAPH::buildConnectionGraph()
  566. {
  567. // Recache all bus aliases for later use
  568. wxCHECK_RET( m_schematic, "Connection graph cannot be built without schematic pointer" );
  569. SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
  570. for( unsigned i = 0; i < all_sheets.size(); i++ )
  571. {
  572. for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() )
  573. m_bus_alias_cache[ alias->GetName() ] = alias;
  574. }
  575. // Build subgraphs from items (on a per-sheet basis)
  576. for( SCH_ITEM* item : m_items )
  577. {
  578. for( const auto& it : item->m_connection_map )
  579. {
  580. const auto sheet = it.first;
  581. auto connection = it.second;
  582. if( connection->SubgraphCode() == 0 )
  583. {
  584. CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
  585. subgraph->m_code = m_last_subgraph_code++;
  586. subgraph->m_sheet = sheet;
  587. subgraph->AddItem( item );
  588. connection->SetSubgraphCode( subgraph->m_code );
  589. m_item_to_subgraph_map[item] = subgraph;
  590. std::list<SCH_ITEM*> members;
  591. auto get_items =
  592. [&]( SCH_ITEM* aItem ) -> bool
  593. {
  594. SCH_CONNECTION* conn = aItem->Connection( &sheet );
  595. if( !conn )
  596. conn = aItem->InitializeConnection( sheet, this );
  597. return ( conn->SubgraphCode() == 0 );
  598. };
  599. std::copy_if( item->ConnectedItems( sheet ).begin(),
  600. item->ConnectedItems( sheet ).end(),
  601. std::back_inserter( members ), get_items );
  602. for( SCH_ITEM* connected_item : members )
  603. {
  604. if( connected_item->Type() == SCH_NO_CONNECT_T )
  605. subgraph->m_no_connect = connected_item;
  606. SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
  607. wxASSERT( connected_conn );
  608. if( connected_conn->SubgraphCode() == 0 )
  609. {
  610. connected_conn->SetSubgraphCode( subgraph->m_code );
  611. m_item_to_subgraph_map[connected_item] = subgraph;
  612. subgraph->AddItem( connected_item );
  613. std::copy_if( connected_item->ConnectedItems( sheet ).begin(),
  614. connected_item->ConnectedItems( sheet ).end(),
  615. std::back_inserter( members ), get_items );
  616. }
  617. }
  618. subgraph->m_dirty = true;
  619. m_subgraphs.push_back( subgraph );
  620. }
  621. }
  622. }
  623. /**
  624. * TODO(JE): Net codes are non-deterministic. Fortunately, they are also not really used for
  625. * anything. We should consider removing them entirely and just using net names everywhere.
  626. */
  627. // Resolve drivers for subgraphs and propagate connectivity info
  628. // We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
  629. size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
  630. ( m_subgraphs.size() + 3 ) / 4 );
  631. std::atomic<size_t> nextSubgraph( 0 );
  632. std::vector<std::future<size_t>> returns( parallelThreadCount );
  633. std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
  634. std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
  635. [&] ( const CONNECTION_SUBGRAPH* candidate )
  636. {
  637. return candidate->m_dirty;
  638. } );
  639. auto update_lambda = [&nextSubgraph, &dirty_graphs]() -> size_t
  640. {
  641. for( size_t subgraphId = nextSubgraph++; subgraphId < dirty_graphs.size(); subgraphId = nextSubgraph++ )
  642. {
  643. auto subgraph = dirty_graphs[subgraphId];
  644. if( !subgraph->m_dirty )
  645. continue;
  646. // Special processing for some items
  647. for( auto item : subgraph->m_items )
  648. {
  649. switch( item->Type() )
  650. {
  651. case SCH_NO_CONNECT_T:
  652. subgraph->m_no_connect = item;
  653. break;
  654. case SCH_BUS_WIRE_ENTRY_T:
  655. subgraph->m_bus_entry = item;
  656. break;
  657. case SCH_PIN_T:
  658. {
  659. auto pin = static_cast<SCH_PIN*>( item );
  660. if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
  661. subgraph->m_no_connect = item;
  662. break;
  663. }
  664. default:
  665. break;
  666. }
  667. }
  668. subgraph->ResolveDrivers( true );
  669. subgraph->m_dirty = false;
  670. }
  671. return 1;
  672. };
  673. if( parallelThreadCount == 1 )
  674. update_lambda();
  675. else
  676. {
  677. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  678. returns[ii] = std::async( std::launch::async, update_lambda );
  679. // Finalize the threads
  680. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  681. returns[ii].wait();
  682. }
  683. // Now discard any non-driven subgraphs from further consideration
  684. std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
  685. [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
  686. {
  687. return candidate->m_driver;
  688. } );
  689. // Check for subgraphs with the same net name but only weak drivers.
  690. // For example, two wires that are both connected to hierarchical
  691. // sheet pins that happen to have the same name, but are not the same.
  692. for( auto&& subgraph : m_driver_subgraphs )
  693. {
  694. wxString full_name = subgraph->m_driver_connection->Name();
  695. wxString name = subgraph->m_driver_connection->Name( true );
  696. m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
  697. // For vector buses, we need to cache the prefix also, as two different instances of the
  698. // weakly driven pin may have the same prefix but different vector start and end. We need
  699. // to treat those as needing renaming also, because otherwise if they end up on a sheet with
  700. // common usage, they will be incorrectly merged.
  701. if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
  702. {
  703. wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
  704. m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
  705. }
  706. subgraph->m_dirty = true;
  707. if( subgraph->m_strong_driver )
  708. {
  709. SCH_ITEM* driver = subgraph->m_driver;
  710. SCH_SHEET_PATH sheet = subgraph->m_sheet;
  711. switch( driver->Type() )
  712. {
  713. case SCH_LABEL_T:
  714. case SCH_HIER_LABEL_T:
  715. {
  716. m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
  717. break;
  718. }
  719. case SCH_GLOBAL_LABEL_T:
  720. {
  721. m_global_label_cache[name].push_back( subgraph );
  722. break;
  723. }
  724. case SCH_PIN_T:
  725. {
  726. auto pin = static_cast<SCH_PIN*>( driver );
  727. wxASSERT( pin->IsPowerConnection() );
  728. m_global_label_cache[name].push_back( subgraph );
  729. break;
  730. }
  731. default:
  732. wxLogTrace( ConnTrace, "Unexpected strong driver %s",
  733. driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ) );
  734. break;
  735. }
  736. }
  737. }
  738. // Generate subgraphs for invisible power pins. These will be merged with other subgraphs
  739. // on the same sheet in the next loop.
  740. std::unordered_map<int, CONNECTION_SUBGRAPH*> invisible_pin_subgraphs;
  741. for( const auto& it : m_invisible_power_pins )
  742. {
  743. SCH_SHEET_PATH sheet = it.first;
  744. SCH_PIN* pin = it.second;
  745. if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParent()->IsPower() )
  746. {
  747. // ERC will warn about this: user has wired up an invisible pin
  748. continue;
  749. }
  750. SCH_CONNECTION* connection = pin->Connection( &sheet );
  751. if( !connection )
  752. connection = pin->InitializeConnection( sheet, this );
  753. // If this pin already has a subgraph, don't need to process
  754. if( connection->SubgraphCode() > 0 )
  755. continue;
  756. connection->SetName( pin->GetShownName() );
  757. int code = assignNewNetCode( *connection );
  758. connection->SetNetCode( code );
  759. CONNECTION_SUBGRAPH* subgraph;
  760. auto jj = invisible_pin_subgraphs.find( code );
  761. if( jj != invisible_pin_subgraphs.end() )
  762. {
  763. subgraph = jj->second;
  764. subgraph->AddItem( pin );
  765. }
  766. else
  767. {
  768. subgraph = new CONNECTION_SUBGRAPH( this );
  769. subgraph->m_code = m_last_subgraph_code++;
  770. subgraph->m_sheet = sheet;
  771. subgraph->AddItem( pin );
  772. subgraph->ResolveDrivers();
  773. auto key = std::make_pair( subgraph->GetNetName(), code );
  774. m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
  775. m_subgraphs.push_back( subgraph );
  776. m_driver_subgraphs.push_back( subgraph );
  777. invisible_pin_subgraphs[code] = subgraph;
  778. }
  779. connection->SetSubgraphCode( subgraph->m_code );
  780. }
  781. // Here we do all the local (sheet) processing of each subgraph, including assigning net
  782. // codes, merging subgraphs together that use label connections, etc.
  783. // Cache remaining valid subgraphs by sheet path
  784. for( auto subgraph : m_driver_subgraphs )
  785. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
  786. std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
  787. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  788. {
  789. if( subgraph->m_absorbed )
  790. continue;
  791. SCH_CONNECTION* connection = subgraph->m_driver_connection;
  792. SCH_SHEET_PATH sheet = subgraph->m_sheet;
  793. wxString name = connection->Name();
  794. // Test subgraphs with weak drivers for net name conflicts and fix them
  795. unsigned suffix = 1;
  796. auto create_new_name =
  797. [&suffix]( SCH_CONNECTION* aConn ) -> wxString
  798. {
  799. wxString newName;
  800. // For group buses with a prefix, we can add the suffix to the prefix.
  801. // If they don't have a prefix, we force the creation of a prefix so that
  802. // two buses don't get inadvertently shorted together.
  803. if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
  804. {
  805. wxString prefix = aConn->BusPrefix();
  806. if( prefix.empty() )
  807. prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
  808. wxString oldName = aConn->Name().AfterFirst( '{' );
  809. newName = wxString::Format( "%s_%u{%s", prefix, suffix, oldName );
  810. aConn->ConfigureFromLabel( newName );
  811. }
  812. else
  813. {
  814. newName = wxString::Format( "%s_%u", aConn->Name(), suffix );
  815. aConn->SetSuffix( wxString::Format( "_%u", suffix ) );
  816. }
  817. suffix++;
  818. return newName;
  819. };
  820. if( !subgraph->m_strong_driver )
  821. {
  822. std::vector<CONNECTION_SUBGRAPH*>* vec = &m_net_name_to_subgraphs_map.at( name );
  823. // If we are a unique bus vector, check if we aren't actually unique because of another
  824. // subgraph with a similar bus vector
  825. if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
  826. {
  827. wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
  828. vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
  829. }
  830. if( vec->size() > 1 )
  831. {
  832. wxString new_name = create_new_name( connection );
  833. while( m_net_name_to_subgraphs_map.count( new_name ) )
  834. new_name = create_new_name( connection );
  835. wxLogTrace( ConnTrace, "%ld (%s) is weakly driven and not unique. Changing to %s.",
  836. subgraph->m_code, name, new_name );
  837. alg::delete_matching( *vec, subgraph );
  838. m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
  839. name = new_name;
  840. }
  841. else
  842. {
  843. // If there is no conflict, promote sheet pins to be strong drivers so that they
  844. // will be considered below for propagation/merging.
  845. // It is possible for this to generate a conflict if the sheet pin has the same
  846. // name as a global label on the same sheet, because global merging will then treat
  847. // this subgraph as if it had a matching local label. So, for those cases, we
  848. // don't apply this promotion
  849. if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
  850. {
  851. bool conflict = false;
  852. wxString global_name = connection->Name( true );
  853. auto kk = m_net_name_to_subgraphs_map.find( global_name );
  854. if( kk != m_net_name_to_subgraphs_map.end() )
  855. {
  856. // A global will conflict if it is on the same sheet as this subgraph, since
  857. // it would be connected by implicit local label linking
  858. std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
  859. for( const CONNECTION_SUBGRAPH* candidate : candidates )
  860. {
  861. if( candidate->m_sheet == sheet )
  862. conflict = true;
  863. }
  864. }
  865. if( conflict )
  866. {
  867. wxLogTrace( ConnTrace,
  868. "%ld (%s) skipped for promotion due to potential conflict",
  869. subgraph->m_code, name );
  870. }
  871. else
  872. {
  873. wxLogTrace( ConnTrace,
  874. "%ld (%s) weakly driven by unique sheet pin %s, promoting",
  875. subgraph->m_code, name,
  876. subgraph->m_driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ) );
  877. subgraph->m_strong_driver = true;
  878. }
  879. }
  880. }
  881. }
  882. // Assign net codes
  883. if( connection->IsBus() )
  884. {
  885. int code = -1;
  886. auto it = m_bus_name_to_code_map.find( name );
  887. if( it != m_bus_name_to_code_map.end() )
  888. {
  889. code = it->second;
  890. }
  891. else
  892. {
  893. code = m_last_bus_code++;
  894. m_bus_name_to_code_map[ name ] = code;
  895. }
  896. connection->SetBusCode( code );
  897. assignNetCodesToBus( connection );
  898. }
  899. else
  900. {
  901. assignNewNetCode( *connection );
  902. }
  903. // Reset the flag for the next loop below
  904. subgraph->m_dirty = true;
  905. // Next, we merge together subgraphs that have label connections, and create
  906. // neighbor links for subgraphs that are part of a bus on the same sheet.
  907. // For merging, we consider each possible strong driver.
  908. // If this subgraph doesn't have a strong driver, let's skip it, since there is no
  909. // way it will be merged with anything.
  910. if( !subgraph->m_strong_driver )
  911. continue;
  912. // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
  913. // as the subgraph we are considering that has a strong driver.
  914. // Weakly driven subgraphs are not considered since they will never be absorbed or
  915. // form neighbor links.
  916. std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
  917. std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
  918. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
  919. std::back_inserter( candidate_subgraphs ),
  920. [&] ( const CONNECTION_SUBGRAPH* candidate )
  921. {
  922. return ( !candidate->m_absorbed &&
  923. candidate->m_strong_driver &&
  924. candidate != subgraph );
  925. } );
  926. // This is a list of connections on the current subgraph to compare to the
  927. // drivers of each candidate subgraph. If the current subgraph is a bus,
  928. // we should consider each bus member.
  929. std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
  930. // Also check the main driving connection
  931. connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
  932. auto add_connections_to_check = [&] ( CONNECTION_SUBGRAPH* aSubgraph ) {
  933. for( SCH_ITEM* possible_driver : aSubgraph->m_items )
  934. {
  935. if( possible_driver == aSubgraph->m_driver )
  936. continue;
  937. auto c = getDefaultConnection( possible_driver, aSubgraph );
  938. if( c )
  939. {
  940. if( c->Type() != aSubgraph->m_driver_connection->Type() )
  941. continue;
  942. if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
  943. continue;
  944. connections_to_check.push_back( c );
  945. wxLogTrace( ConnTrace,
  946. "%lu (%s): Adding secondary driver %s", aSubgraph->m_code,
  947. aSubgraph->m_driver_connection->Name( true ), c->Name( true ) );
  948. }
  949. }
  950. };
  951. // Now add other strong drivers
  952. // The actual connection attached to these items will have been overwritten
  953. // by the chosen driver of the subgraph, so we need to create a dummy connection
  954. add_connections_to_check( subgraph );
  955. for( unsigned i = 0; i < connections_to_check.size(); i++ )
  956. {
  957. auto member = connections_to_check[i];
  958. if( member->IsBus() )
  959. {
  960. connections_to_check.insert( connections_to_check.end(),
  961. member->Members().begin(),
  962. member->Members().end() );
  963. }
  964. wxString test_name = member->Name( true );
  965. for( auto candidate : candidate_subgraphs )
  966. {
  967. if( candidate->m_absorbed )
  968. continue;
  969. bool match = false;
  970. if( candidate->m_driver_connection->Name( true ) == test_name )
  971. {
  972. match = true;
  973. }
  974. else
  975. {
  976. if( !candidate->m_multiple_drivers )
  977. continue;
  978. for( SCH_ITEM *driver : candidate->m_drivers )
  979. {
  980. if( driver == candidate->m_driver )
  981. continue;
  982. // Sheet pins are not candidates for merging
  983. if( driver->Type() == SCH_SHEET_PIN_T )
  984. continue;
  985. if( driver->Type() == SCH_PIN_T )
  986. {
  987. auto pin = static_cast<SCH_PIN*>( driver );
  988. if( pin->IsPowerConnection() && pin->GetShownName() == test_name )
  989. {
  990. match = true;
  991. break;
  992. }
  993. }
  994. else
  995. {
  996. wxASSERT( driver->Type() == SCH_LABEL_T ||
  997. driver->Type() == SCH_GLOBAL_LABEL_T ||
  998. driver->Type() == SCH_HIER_LABEL_T );
  999. if( subgraph->GetNameForDriver( driver ) == test_name )
  1000. {
  1001. match = true;
  1002. break;
  1003. }
  1004. }
  1005. }
  1006. }
  1007. if( match )
  1008. {
  1009. if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
  1010. {
  1011. wxLogTrace( ConnTrace, "%lu (%s) has bus child %lu (%s)", subgraph->m_code,
  1012. connection->Name(), candidate->m_code, member->Name() );
  1013. subgraph->m_bus_neighbors[member].insert( candidate );
  1014. candidate->m_bus_parents[member].insert( subgraph );
  1015. }
  1016. else
  1017. {
  1018. wxLogTrace( ConnTrace, "%lu (%s) absorbs neighbor %lu (%s)",
  1019. subgraph->m_code, connection->Name(),
  1020. candidate->m_code, candidate->m_driver_connection->Name() );
  1021. // Candidate may have other non-chosen drivers we need to follow
  1022. add_connections_to_check( candidate );
  1023. subgraph->Absorb( candidate );
  1024. invalidated_subgraphs.insert( subgraph );
  1025. }
  1026. }
  1027. }
  1028. }
  1029. }
  1030. // Update any subgraph that was invalidated above
  1031. for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
  1032. {
  1033. if( subgraph->m_absorbed )
  1034. continue;
  1035. subgraph->ResolveDrivers();
  1036. if( subgraph->m_driver_connection->IsBus() )
  1037. assignNetCodesToBus( subgraph->m_driver_connection );
  1038. else
  1039. assignNewNetCode( *subgraph->m_driver_connection );
  1040. wxLogTrace( ConnTrace, "Re-resolving drivers for %lu (%s)", subgraph->m_code,
  1041. subgraph->m_driver_connection->Name() );
  1042. }
  1043. // Absorbed subgraphs should no longer be considered
  1044. alg::delete_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool
  1045. {
  1046. return candidate->m_absorbed;
  1047. } );
  1048. // Store global subgraphs for later reference
  1049. std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
  1050. std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
  1051. std::back_inserter( global_subgraphs ),
  1052. [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
  1053. {
  1054. return !candidate->m_local_driver;
  1055. } );
  1056. // Recache remaining valid subgraphs by sheet path
  1057. m_sheet_to_subgraphs_map.clear();
  1058. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1059. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
  1060. // Update item connections at this point so that neighbor propagation works
  1061. nextSubgraph.store( 0 );
  1062. auto preliminaryUpdateTask =
  1063. [&]() -> size_t
  1064. {
  1065. for( size_t subgraphId = nextSubgraph++;
  1066. subgraphId < m_driver_subgraphs.size();
  1067. subgraphId = nextSubgraph++ )
  1068. {
  1069. m_driver_subgraphs[subgraphId]->UpdateItemConnections();
  1070. }
  1071. return 1;
  1072. };
  1073. if( parallelThreadCount == 1 )
  1074. preliminaryUpdateTask();
  1075. else
  1076. {
  1077. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  1078. returns[ii] = std::async( std::launch::async, preliminaryUpdateTask );
  1079. // Finalize the threads
  1080. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  1081. returns[ii].wait();
  1082. }
  1083. // Next time through the subgraphs, we do some post-processing to handle things like
  1084. // connecting bus members to their neighboring subgraphs, and then propagate connections
  1085. // through the hierarchy
  1086. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1087. {
  1088. if( !subgraph->m_dirty )
  1089. continue;
  1090. wxLogTrace( ConnTrace, "Processing %lu (%s) for propagation", subgraph->m_code,
  1091. subgraph->m_driver_connection->Name() );
  1092. // For subgraphs that are driven by a global (power port or label) and have more
  1093. // than one global driver, we need to seek out other subgraphs driven by the
  1094. // same name as the non-chosen driver and update them to match the chosen one.
  1095. if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
  1096. {
  1097. for( SCH_ITEM* driver : subgraph->m_drivers )
  1098. {
  1099. if( driver == subgraph->m_driver )
  1100. continue;
  1101. wxString secondary_name = subgraph->GetNameForDriver( driver );
  1102. if( secondary_name == subgraph->m_driver_connection->Name() )
  1103. continue;
  1104. bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
  1105. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN;
  1106. for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
  1107. {
  1108. if( candidate == subgraph )
  1109. continue;
  1110. if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
  1111. continue;
  1112. SCH_CONNECTION* conn = candidate->m_driver_connection;
  1113. if( conn->Name() == secondary_name )
  1114. {
  1115. wxLogTrace( ConnTrace, "Global %lu (%s) promoted to %s", candidate->m_code,
  1116. conn->Name(), subgraph->m_driver_connection->Name() );
  1117. conn->Clone( *subgraph->m_driver_connection );
  1118. candidate->m_dirty = false;
  1119. }
  1120. }
  1121. }
  1122. }
  1123. // This call will handle descending the hierarchy and updating child subgraphs
  1124. propagateToNeighbors( subgraph );
  1125. }
  1126. // Handle buses that have been linked together somewhere by member (net) connections.
  1127. // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
  1128. // For net subgraphs that have more than one bus parent, we need to ensure that those
  1129. // buses are linked together in the final netlist. The final name of each bus might not
  1130. // match the local name that was used to establish the parent-child relationship, because
  1131. // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
  1132. // we need to identify the appropriate bus members to link together (and their final names),
  1133. // and then update all instances of the old name in the hierarchy.
  1134. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1135. {
  1136. // All SGs should have been processed by propagateToNeighbors above
  1137. wxASSERT_MSG( !subgraph->m_dirty, "Subgraph not processed by propagateToNeighbors!" );
  1138. if( subgraph->m_bus_parents.size() < 2 )
  1139. continue;
  1140. SCH_CONNECTION* conn = subgraph->m_driver_connection;
  1141. wxLogTrace( ConnTrace, "%lu (%s) has multiple bus parents",
  1142. subgraph->m_code, conn->Name() );
  1143. wxASSERT( conn->IsNet() );
  1144. for( const auto& ii : subgraph->m_bus_parents )
  1145. {
  1146. SCH_CONNECTION* link_member = ii.first.get();
  1147. for( CONNECTION_SUBGRAPH* parent : ii.second )
  1148. {
  1149. while( parent->m_absorbed )
  1150. parent = parent->m_absorbed_by;
  1151. SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
  1152. if( !match )
  1153. {
  1154. wxLogTrace( ConnTrace, "Warning: could not match %s inside %lu (%s)",
  1155. conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
  1156. continue;
  1157. }
  1158. if( conn->Name() != match->Name() )
  1159. {
  1160. wxString old_name = match->Name();
  1161. wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", parent->m_code,
  1162. parent->m_driver_connection->Name(), old_name, conn->Name() );
  1163. match->Clone( *conn );
  1164. auto jj = m_net_name_to_subgraphs_map.find( old_name );
  1165. if( jj == m_net_name_to_subgraphs_map.end() )
  1166. continue;
  1167. for( CONNECTION_SUBGRAPH* old_sg : jj->second )
  1168. {
  1169. while( old_sg->m_absorbed )
  1170. old_sg = old_sg->m_absorbed_by;
  1171. old_sg->m_driver_connection->Clone( *conn );
  1172. }
  1173. }
  1174. }
  1175. }
  1176. }
  1177. nextSubgraph.store( 0 );
  1178. auto updateItemConnectionsTask =
  1179. [&]() -> size_t
  1180. {
  1181. for( size_t subgraphId = nextSubgraph++;
  1182. subgraphId < m_driver_subgraphs.size();
  1183. subgraphId = nextSubgraph++ )
  1184. {
  1185. CONNECTION_SUBGRAPH* subgraph = m_driver_subgraphs[subgraphId];
  1186. // Make sure weakly-driven single-pin nets get the unconnected_ prefix
  1187. if( !subgraph->m_strong_driver && subgraph->m_drivers.size() == 1 &&
  1188. subgraph->m_driver->Type() == SCH_PIN_T )
  1189. {
  1190. SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
  1191. wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
  1192. subgraph->m_driver_connection->ConfigureFromLabel( name );
  1193. }
  1194. subgraph->m_dirty = false;
  1195. subgraph->UpdateItemConnections();
  1196. // No other processing to do on buses
  1197. if( subgraph->m_driver_connection->IsBus() )
  1198. continue;
  1199. // As a visual aid, we can check sheet pins that are driven by themselves to see
  1200. // if they should be promoted to buses
  1201. if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
  1202. {
  1203. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
  1204. if( SCH_SHEET* sheet = pin->GetParent() )
  1205. {
  1206. wxString pinText = pin->GetText();
  1207. SCH_SCREEN* screen = sheet->GetScreen();
  1208. for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
  1209. {
  1210. SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
  1211. if( label->GetText() == pinText )
  1212. {
  1213. SCH_SHEET_PATH path = subgraph->m_sheet;
  1214. path.push_back( sheet );
  1215. SCH_CONNECTION* parent_conn = label->Connection( &path );
  1216. if( parent_conn && parent_conn->IsBus() )
  1217. subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
  1218. break;
  1219. }
  1220. }
  1221. if( subgraph->m_driver_connection->IsBus() )
  1222. continue;
  1223. }
  1224. }
  1225. }
  1226. return 1;
  1227. };
  1228. if( parallelThreadCount == 1 )
  1229. updateItemConnectionsTask();
  1230. else
  1231. {
  1232. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  1233. returns[ii] = std::async( std::launch::async, updateItemConnectionsTask );
  1234. // Finalize the threads
  1235. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  1236. returns[ii].wait();
  1237. }
  1238. m_net_code_to_subgraphs_map.clear();
  1239. m_net_name_to_subgraphs_map.clear();
  1240. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1241. {
  1242. auto key = std::make_pair( subgraph->GetNetName(),
  1243. subgraph->m_driver_connection->NetCode() );
  1244. m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
  1245. m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
  1246. }
  1247. }
  1248. int CONNECTION_GRAPH::assignNewNetCode( SCH_CONNECTION& aConnection )
  1249. {
  1250. int code;
  1251. auto it = m_net_name_to_code_map.find( aConnection.Name() );
  1252. if( it == m_net_name_to_code_map.end() )
  1253. {
  1254. code = m_last_net_code++;
  1255. m_net_name_to_code_map[ aConnection.Name() ] = code;
  1256. }
  1257. else
  1258. {
  1259. code = it->second;
  1260. }
  1261. aConnection.SetNetCode( code );
  1262. return code;
  1263. }
  1264. void CONNECTION_GRAPH::assignNetCodesToBus( SCH_CONNECTION* aConnection )
  1265. {
  1266. auto connections_to_check( aConnection->Members() );
  1267. for( unsigned i = 0; i < connections_to_check.size(); i++ )
  1268. {
  1269. auto member = connections_to_check[i];
  1270. if( member->IsBus() )
  1271. {
  1272. connections_to_check.insert( connections_to_check.end(),
  1273. member->Members().begin(),
  1274. member->Members().end() );
  1275. continue;
  1276. }
  1277. assignNewNetCode( *member );
  1278. }
  1279. }
  1280. void CONNECTION_GRAPH::propagateToNeighbors( CONNECTION_SUBGRAPH* aSubgraph )
  1281. {
  1282. SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
  1283. std::vector<CONNECTION_SUBGRAPH*> search_list;
  1284. std::unordered_set<CONNECTION_SUBGRAPH*> visited;
  1285. std::vector<SCH_CONNECTION*> stale_bus_members;
  1286. auto visit =
  1287. [&]( CONNECTION_SUBGRAPH* aParent )
  1288. {
  1289. for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
  1290. {
  1291. SCH_SHEET_PATH path = aParent->m_sheet;
  1292. path.push_back( pin->GetParent() );
  1293. auto it = m_sheet_to_subgraphs_map.find( path );
  1294. if( it == m_sheet_to_subgraphs_map.end() )
  1295. continue;
  1296. for( CONNECTION_SUBGRAPH* candidate : it->second )
  1297. {
  1298. if( !candidate->m_strong_driver
  1299. || candidate->m_hier_ports.empty()
  1300. || visited.count( candidate ) )
  1301. {
  1302. continue;
  1303. }
  1304. for( SCH_HIERLABEL* label : candidate->m_hier_ports )
  1305. {
  1306. if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
  1307. {
  1308. wxLogTrace( ConnTrace, "%lu: found child %lu (%s)", aParent->m_code,
  1309. candidate->m_code, candidate->m_driver_connection->Name() );
  1310. candidate->m_hier_parent = aParent;
  1311. search_list.push_back( candidate );
  1312. break;
  1313. }
  1314. }
  1315. }
  1316. }
  1317. for( SCH_HIERLABEL* label : aParent->m_hier_ports )
  1318. {
  1319. SCH_SHEET_PATH path = aParent->m_sheet;
  1320. path.pop_back();
  1321. auto it = m_sheet_to_subgraphs_map.find( path );
  1322. if( it == m_sheet_to_subgraphs_map.end() )
  1323. continue;
  1324. for( CONNECTION_SUBGRAPH* candidate : it->second )
  1325. {
  1326. if( candidate->m_hier_pins.empty()
  1327. || visited.count( candidate )
  1328. || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
  1329. {
  1330. continue;
  1331. }
  1332. for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
  1333. {
  1334. SCH_SHEET_PATH pin_path = path;
  1335. pin_path.push_back( pin->GetParent() );
  1336. if( pin_path != aParent->m_sheet )
  1337. continue;
  1338. if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
  1339. {
  1340. wxLogTrace( ConnTrace, "%lu: found additional parent %lu (%s)",
  1341. aParent->m_code, candidate->m_code,
  1342. candidate->m_driver_connection->Name() );
  1343. search_list.push_back( candidate );
  1344. break;
  1345. }
  1346. }
  1347. }
  1348. }
  1349. };
  1350. auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
  1351. for( const auto& kv : aParentGraph->m_bus_neighbors )
  1352. {
  1353. for( CONNECTION_SUBGRAPH* neighbor : kv.second )
  1354. {
  1355. // May have been absorbed but won't have been deleted
  1356. while( neighbor->m_absorbed )
  1357. neighbor = neighbor->m_absorbed_by;
  1358. SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
  1359. // Now member may be out of date, since we just cloned the
  1360. // connection from higher up in the hierarchy. We need to
  1361. // figure out what the actual new connection is.
  1362. SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
  1363. if( !member )
  1364. {
  1365. // Try harder: we might match on a secondary driver
  1366. for( CONNECTION_SUBGRAPH* sg : kv.second )
  1367. {
  1368. if( sg->m_multiple_drivers )
  1369. {
  1370. SCH_SHEET_PATH sheet = sg->m_sheet;
  1371. for( SCH_ITEM* driver : sg->m_drivers )
  1372. {
  1373. auto c = getDefaultConnection( driver, sg );
  1374. member = matchBusMember( parent, c.get() );
  1375. if( member )
  1376. break;
  1377. }
  1378. }
  1379. if( member )
  1380. break;
  1381. }
  1382. }
  1383. // This is bad, probably an ERC error
  1384. if( !member )
  1385. {
  1386. wxLogTrace( ConnTrace, "Could not match bus member %s in %s",
  1387. kv.first->Name(), parent->Name() );
  1388. continue;
  1389. }
  1390. auto neighbor_conn = neighbor->m_driver_connection;
  1391. auto neighbor_name = neighbor_conn->Name();
  1392. // Matching name: no update needed
  1393. if( neighbor_name == member->Name() )
  1394. continue;
  1395. // Was this neighbor already updated from a different sheet? Don't rename it again
  1396. if( neighbor_conn->Sheet() != neighbor->m_sheet )
  1397. continue;
  1398. // Safety check against infinite recursion
  1399. wxASSERT( neighbor_conn->IsNet() );
  1400. wxLogTrace( ConnTrace, "%lu (%s) connected to bus member %s (local %s)",
  1401. neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
  1402. // Take whichever name is higher priority
  1403. if( CONNECTION_SUBGRAPH::GetDriverPriority( neighbor->m_driver )
  1404. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  1405. {
  1406. member->Clone( *neighbor_conn );
  1407. stale_bus_members.push_back( member );
  1408. }
  1409. else
  1410. {
  1411. neighbor_conn->Clone( *member );
  1412. recacheSubgraphName( neighbor, neighbor_name );
  1413. // Recurse onto this neighbor in case it needs to re-propagate
  1414. neighbor->m_dirty = true;
  1415. propagateToNeighbors( neighbor );
  1416. }
  1417. }
  1418. }
  1419. };
  1420. // If we are a bus, we must propagate to local neighbors and then the hierarchy
  1421. if( conn->IsBus() )
  1422. propagate_bus_neighbors( aSubgraph );
  1423. // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
  1424. // If we only have one or the other, process (we can either go bottom-up or top-down depending
  1425. // on which subgraph comes up first)
  1426. if( !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
  1427. {
  1428. wxLogTrace( ConnTrace, "%lu (%s) has both hier ports and pins; deferring processing",
  1429. aSubgraph->m_code, conn->Name() );
  1430. return;
  1431. }
  1432. else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
  1433. {
  1434. wxLogTrace( ConnTrace, "%lu (%s) has no hier pins or ports; marking clean",
  1435. aSubgraph->m_code, conn->Name() );
  1436. aSubgraph->m_dirty = false;
  1437. return;
  1438. }
  1439. visited.insert( aSubgraph );
  1440. wxLogTrace( ConnTrace, "Propagating %lu (%s) to subsheets",
  1441. aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
  1442. visit( aSubgraph );
  1443. for( unsigned i = 0; i < search_list.size(); i++ )
  1444. {
  1445. auto child = search_list[i];
  1446. visited.insert( child );
  1447. visit( child );
  1448. child->m_dirty = false;
  1449. }
  1450. // Now, find the best driver for this chain of subgraphs
  1451. CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
  1452. CONNECTION_SUBGRAPH::PRIORITY highest =
  1453. CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver );
  1454. bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
  1455. wxString bestName = aSubgraph->m_driver_connection->Name();
  1456. // Check if a subsheet has a higher-priority connection to the same net
  1457. if( highest < CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  1458. {
  1459. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1460. {
  1461. if( subgraph == aSubgraph )
  1462. continue;
  1463. CONNECTION_SUBGRAPH::PRIORITY priority =
  1464. CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
  1465. bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
  1466. wxString candidateName = subgraph->m_driver_connection->Name();
  1467. bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
  1468. bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
  1469. // Pick a better driving subgraph if it:
  1470. // a) has a power pin or global driver
  1471. // b) is a strong driver and we're a weak driver
  1472. // c) is a higher priority strong driver
  1473. // d) matches our priority, is a strong driver, and has a shorter path
  1474. // e) matches our strength and is at least as short, and is alphabetically lower
  1475. if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
  1476. ( !bestIsStrong && candidateStrong ) ||
  1477. ( priority > highest && candidateStrong ) ||
  1478. ( priority == highest && candidateStrong && shorterPath ) ||
  1479. ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
  1480. ( candidateName < bestName ) ) )
  1481. {
  1482. bestDriver = subgraph;
  1483. highest = priority;
  1484. bestIsStrong = candidateStrong;
  1485. bestName = candidateName;
  1486. }
  1487. }
  1488. }
  1489. if( bestDriver != aSubgraph )
  1490. {
  1491. wxLogTrace( ConnTrace, "%lu (%s) overridden by new driver %lu (%s)",
  1492. aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
  1493. bestDriver->m_driver_connection->Name() );
  1494. }
  1495. conn = bestDriver->m_driver_connection;
  1496. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1497. {
  1498. wxString old_name = subgraph->m_driver_connection->Name();
  1499. subgraph->m_driver_connection->Clone( *conn );
  1500. if( old_name != conn->Name() )
  1501. recacheSubgraphName( subgraph, old_name );
  1502. if( conn->IsBus() )
  1503. propagate_bus_neighbors( subgraph );
  1504. }
  1505. // Somewhere along the way, a bus member may have been upgraded to a global or power label.
  1506. // Because this can happen anywhere, we need a second pass to update all instances of that bus
  1507. // member to have the correct connection info
  1508. if( conn->IsBus() && !stale_bus_members.empty() )
  1509. {
  1510. for( auto stale_member : stale_bus_members )
  1511. {
  1512. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1513. {
  1514. SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
  1515. stale_member );
  1516. if( !member )
  1517. {
  1518. wxLogTrace( ConnTrace, "WARNING: failed to match stale member %s in %s.",
  1519. stale_member->Name(), subgraph->m_driver_connection->Name() );
  1520. continue;
  1521. }
  1522. wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", subgraph->m_code,
  1523. subgraph->m_driver_connection->Name(), member->LocalName(),
  1524. stale_member->Name() );
  1525. member->Clone( *stale_member );
  1526. propagate_bus_neighbors( subgraph );
  1527. }
  1528. }
  1529. }
  1530. aSubgraph->m_dirty = false;
  1531. }
  1532. std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
  1533. CONNECTION_SUBGRAPH* aSubgraph )
  1534. {
  1535. auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
  1536. switch( aItem->Type() )
  1537. {
  1538. case SCH_PIN_T:
  1539. {
  1540. auto pin = static_cast<SCH_PIN*>( aItem );
  1541. if( pin->IsPowerConnection() )
  1542. c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
  1543. break;
  1544. }
  1545. case SCH_GLOBAL_LABEL_T:
  1546. case SCH_HIER_LABEL_T:
  1547. case SCH_LABEL_T:
  1548. {
  1549. c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
  1550. break;
  1551. }
  1552. default:
  1553. break;
  1554. }
  1555. if( c )
  1556. {
  1557. c->SetGraph( this );
  1558. c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
  1559. }
  1560. return c;
  1561. }
  1562. SCH_CONNECTION* CONNECTION_GRAPH::matchBusMember( SCH_CONNECTION* aBusConnection,
  1563. SCH_CONNECTION* aSearch )
  1564. {
  1565. wxASSERT( aBusConnection->IsBus() );
  1566. SCH_CONNECTION* match = nullptr;
  1567. if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
  1568. {
  1569. // Vector bus: compare against index, because we allow the name
  1570. // to be different
  1571. for( const auto& bus_member : aBusConnection->Members() )
  1572. {
  1573. if( bus_member->VectorIndex() == aSearch->VectorIndex() )
  1574. {
  1575. match = bus_member.get();
  1576. break;
  1577. }
  1578. }
  1579. }
  1580. else
  1581. {
  1582. // Group bus
  1583. for( const auto& c : aBusConnection->Members() )
  1584. {
  1585. // Vector inside group: compare names, because for bus groups
  1586. // we expect the naming to be consistent across all usages
  1587. // TODO(JE) explain this in the docs
  1588. if( c->Type() == CONNECTION_TYPE::BUS )
  1589. {
  1590. for( const auto& bus_member : c->Members() )
  1591. {
  1592. if( bus_member->LocalName() == aSearch->LocalName() )
  1593. {
  1594. match = bus_member.get();
  1595. break;
  1596. }
  1597. }
  1598. }
  1599. else if( c->LocalName() == aSearch->LocalName() )
  1600. {
  1601. match = c.get();
  1602. break;
  1603. }
  1604. }
  1605. }
  1606. return match;
  1607. }
  1608. void CONNECTION_GRAPH::recacheSubgraphName( CONNECTION_SUBGRAPH* aSubgraph,
  1609. const wxString& aOldName )
  1610. {
  1611. auto it = m_net_name_to_subgraphs_map.find( aOldName );
  1612. if( it != m_net_name_to_subgraphs_map.end() )
  1613. {
  1614. std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
  1615. alg::delete_matching( vec, aSubgraph );
  1616. }
  1617. wxLogTrace( ConnTrace, "recacheSubgraphName: %s => %s", aOldName,
  1618. aSubgraph->m_driver_connection->Name() );
  1619. m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
  1620. }
  1621. std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
  1622. {
  1623. auto it = m_bus_alias_cache.find( aName );
  1624. return it != m_bus_alias_cache.end() ? it->second : nullptr;
  1625. }
  1626. std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
  1627. {
  1628. std::vector<const CONNECTION_SUBGRAPH*> ret;
  1629. for( auto&& subgraph : m_subgraphs )
  1630. {
  1631. // Graph is supposed to be up-to-date before calling this
  1632. wxASSERT( !subgraph->m_dirty );
  1633. if( !subgraph->m_driver )
  1634. continue;
  1635. auto sheet = subgraph->m_sheet;
  1636. auto connection = subgraph->m_driver->Connection( &sheet );
  1637. if( !connection->IsBus() )
  1638. continue;
  1639. auto labels = subgraph->GetBusLabels();
  1640. if( labels.size() > 1 )
  1641. {
  1642. bool different = false;
  1643. wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
  1644. for( unsigned i = 1; i < labels.size(); ++i )
  1645. {
  1646. if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
  1647. {
  1648. different = true;
  1649. break;
  1650. }
  1651. }
  1652. if( !different )
  1653. continue;
  1654. wxLogTrace( ConnTrace, "SG %ld (%s) has multiple bus labels", subgraph->m_code,
  1655. connection->Name() );
  1656. ret.push_back( subgraph );
  1657. }
  1658. }
  1659. return ret;
  1660. }
  1661. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::FindSubgraphByName( const wxString& aNetName,
  1662. const SCH_SHEET_PATH& aPath )
  1663. {
  1664. auto it = m_net_name_to_subgraphs_map.find( aNetName );
  1665. if( it == m_net_name_to_subgraphs_map.end() )
  1666. return nullptr;
  1667. for( CONNECTION_SUBGRAPH* sg : it->second )
  1668. {
  1669. // Cache is supposed to be valid by now
  1670. wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
  1671. if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
  1672. return sg;
  1673. }
  1674. return nullptr;
  1675. }
  1676. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::FindFirstSubgraphByName( const wxString& aNetName )
  1677. {
  1678. auto it = m_net_name_to_subgraphs_map.find( aNetName );
  1679. if( it == m_net_name_to_subgraphs_map.end() )
  1680. return nullptr;
  1681. wxASSERT( !it->second.empty() );
  1682. return it->second[0];
  1683. }
  1684. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::GetSubgraphForItem( SCH_ITEM* aItem )
  1685. {
  1686. auto it = m_item_to_subgraph_map.find( aItem );
  1687. CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
  1688. while( ret && ret->m_absorbed )
  1689. ret = ret->m_absorbed_by;
  1690. return ret;
  1691. }
  1692. int CONNECTION_GRAPH::RunERC()
  1693. {
  1694. int error_count = 0;
  1695. wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::RunERC" );
  1696. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  1697. // We don't want to run many ERC checks more than once on a given screen even though it may
  1698. // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
  1699. std::set<SCH_ITEM*> seenDriverInstances;
  1700. for( auto&& subgraph : m_subgraphs )
  1701. {
  1702. // Graph is supposed to be up-to-date before calling RunERC()
  1703. wxASSERT( !subgraph->m_dirty );
  1704. if( subgraph->m_absorbed )
  1705. continue;
  1706. if( seenDriverInstances.count( subgraph->m_driver ) )
  1707. continue;
  1708. if( subgraph->m_driver )
  1709. seenDriverInstances.insert( subgraph->m_driver );
  1710. /**
  1711. * NOTE:
  1712. *
  1713. * We could check that labels attached to bus subgraphs follow the
  1714. * proper format (i.e. actually define a bus).
  1715. *
  1716. * This check doesn't need to be here right now because labels
  1717. * won't actually be connected to bus wires if they aren't in the right
  1718. * format due to their TestDanglingEnds() implementation.
  1719. */
  1720. if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
  1721. {
  1722. if( !ercCheckMultipleDrivers( subgraph ) )
  1723. error_count++;
  1724. }
  1725. subgraph->ResolveDrivers( false );
  1726. if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
  1727. {
  1728. if( !ercCheckBusToNetConflicts( subgraph ) )
  1729. error_count++;
  1730. }
  1731. if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
  1732. {
  1733. if( !ercCheckBusToBusEntryConflicts( subgraph ) )
  1734. error_count++;
  1735. }
  1736. if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
  1737. {
  1738. if( !ercCheckBusToBusConflicts( subgraph ) )
  1739. error_count++;
  1740. }
  1741. if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
  1742. {
  1743. if( !ercCheckFloatingWires( subgraph ) )
  1744. error_count++;
  1745. }
  1746. // The following checks are always performed since they don't currently
  1747. // have an option exposed to the user
  1748. if( !ercCheckNoConnects( subgraph ) )
  1749. error_count++;
  1750. if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
  1751. || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
  1752. {
  1753. if( !ercCheckLabels( subgraph ) )
  1754. error_count++;
  1755. }
  1756. }
  1757. // Hierarchical sheet checking is done at the schematic level
  1758. if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
  1759. error_count += ercCheckHierSheets();
  1760. return error_count;
  1761. }
  1762. bool CONNECTION_GRAPH::ercCheckMultipleDrivers( const CONNECTION_SUBGRAPH* aSubgraph )
  1763. {
  1764. if( !aSubgraph->m_second_driver )
  1765. return true;
  1766. SCH_ITEM* primary = aSubgraph->m_first_driver;
  1767. SCH_ITEM* secondary = aSubgraph->m_second_driver;
  1768. wxPoint pos = primary->Type() == SCH_PIN_T ?
  1769. static_cast<SCH_PIN*>( primary )->GetTransformedPosition() :
  1770. primary->GetPosition();
  1771. wxString primaryName = aSubgraph->GetNameForDriver( primary );
  1772. wxString secondaryName = aSubgraph->GetNameForDriver( secondary );
  1773. wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
  1774. "items; %s will be used in the netlist" ),
  1775. primaryName, secondaryName, primaryName );
  1776. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
  1777. ercItem->SetItems( primary, secondary );
  1778. ercItem->SetErrorMessage( msg );
  1779. SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
  1780. aSubgraph->m_sheet.LastScreen()->Append( marker );
  1781. return false;
  1782. }
  1783. bool CONNECTION_GRAPH::ercCheckBusToNetConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  1784. {
  1785. auto sheet = aSubgraph->m_sheet;
  1786. auto screen = sheet.LastScreen();
  1787. SCH_ITEM* net_item = nullptr;
  1788. SCH_ITEM* bus_item = nullptr;
  1789. SCH_CONNECTION conn( this );
  1790. for( SCH_ITEM* item : aSubgraph->m_items )
  1791. {
  1792. switch( item->Type() )
  1793. {
  1794. case SCH_LINE_T:
  1795. {
  1796. if( item->GetLayer() == LAYER_BUS )
  1797. bus_item = ( !bus_item ) ? item : bus_item;
  1798. else
  1799. net_item = ( !net_item ) ? item : net_item;
  1800. break;
  1801. }
  1802. case SCH_LABEL_T:
  1803. case SCH_GLOBAL_LABEL_T:
  1804. case SCH_SHEET_PIN_T:
  1805. case SCH_HIER_LABEL_T:
  1806. {
  1807. SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
  1808. conn.ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
  1809. if( conn.IsBus() )
  1810. bus_item = ( !bus_item ) ? item : bus_item;
  1811. else
  1812. net_item = ( !net_item ) ? item : net_item;
  1813. break;
  1814. }
  1815. default:
  1816. break;
  1817. }
  1818. }
  1819. if( net_item && bus_item )
  1820. {
  1821. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
  1822. ercItem->SetItems( net_item, bus_item );
  1823. SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
  1824. screen->Append( marker );
  1825. return false;
  1826. }
  1827. return true;
  1828. }
  1829. bool CONNECTION_GRAPH::ercCheckBusToBusConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  1830. {
  1831. wxString msg;
  1832. auto sheet = aSubgraph->m_sheet;
  1833. auto screen = sheet.LastScreen();
  1834. SCH_ITEM* label = nullptr;
  1835. SCH_ITEM* port = nullptr;
  1836. for( auto item : aSubgraph->m_items )
  1837. {
  1838. switch( item->Type() )
  1839. {
  1840. case SCH_TEXT_T:
  1841. case SCH_GLOBAL_LABEL_T:
  1842. {
  1843. if( !label && item->Connection( &sheet )->IsBus() )
  1844. label = item;
  1845. break;
  1846. }
  1847. case SCH_SHEET_PIN_T:
  1848. case SCH_HIER_LABEL_T:
  1849. {
  1850. if( !port && item->Connection( &sheet )->IsBus() )
  1851. port = item;
  1852. break;
  1853. }
  1854. default:
  1855. break;
  1856. }
  1857. }
  1858. if( label && port )
  1859. {
  1860. bool match = false;
  1861. for( const auto& member : label->Connection( &sheet )->Members() )
  1862. {
  1863. for( const auto& test : port->Connection( &sheet )->Members() )
  1864. {
  1865. if( test != member && member->Name() == test->Name() )
  1866. {
  1867. match = true;
  1868. break;
  1869. }
  1870. }
  1871. if( match )
  1872. break;
  1873. }
  1874. if( !match )
  1875. {
  1876. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
  1877. ercItem->SetItems( label, port );
  1878. SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
  1879. screen->Append( marker );
  1880. return false;
  1881. }
  1882. }
  1883. return true;
  1884. }
  1885. bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  1886. {
  1887. bool conflict = false;
  1888. auto sheet = aSubgraph->m_sheet;
  1889. auto screen = sheet.LastScreen();
  1890. SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
  1891. SCH_ITEM* bus_wire = nullptr;
  1892. wxString bus_name;
  1893. if( !aSubgraph->m_driver_connection )
  1894. {
  1895. // Incomplete bus entry. Let the unconnected tests handle it.
  1896. return true;
  1897. }
  1898. for( auto item : aSubgraph->m_items )
  1899. {
  1900. switch( item->Type() )
  1901. {
  1902. case SCH_BUS_WIRE_ENTRY_T:
  1903. {
  1904. if( !bus_entry )
  1905. bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  1906. break;
  1907. }
  1908. default:
  1909. break;
  1910. }
  1911. }
  1912. if( bus_entry && bus_entry->m_connected_bus_item )
  1913. {
  1914. bus_wire = bus_entry->m_connected_bus_item;
  1915. wxASSERT( bus_wire->Type() == SCH_LINE_T );
  1916. // In some cases, the connection list (SCH_CONNECTION*) can be null.
  1917. // Skip null connections.
  1918. if( bus_entry->Connection( &sheet )
  1919. && bus_wire->Type() == SCH_LINE_T
  1920. && bus_wire->Connection( &sheet ) )
  1921. {
  1922. conflict = true; // Assume a conflict; we'll reset if we find it's OK
  1923. bus_name = bus_wire->Connection( &sheet )->Name();
  1924. wxString test_name = bus_entry->Connection( &sheet )->Name();
  1925. for( const auto& member : bus_wire->Connection( &sheet )->Members() )
  1926. {
  1927. if( member->Type() == CONNECTION_TYPE::BUS )
  1928. {
  1929. for( const auto& sub_member : member->Members() )
  1930. {
  1931. if( sub_member->Name() == test_name )
  1932. conflict = false;
  1933. }
  1934. }
  1935. else if( member->Name() == test_name )
  1936. {
  1937. conflict = false;
  1938. }
  1939. }
  1940. }
  1941. }
  1942. // Don't report warnings if this bus member has been overridden by a higher priority power pin
  1943. // or global label
  1944. if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
  1945. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  1946. conflict = false;
  1947. if( conflict )
  1948. {
  1949. wxString netName = aSubgraph->m_driver_connection->Name();
  1950. wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
  1951. " member of that bus" ),
  1952. UnescapeString( netName ),
  1953. UnescapeString( bus_name ) );
  1954. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
  1955. ercItem->SetItems( bus_entry, bus_wire );
  1956. ercItem->SetErrorMessage( msg );
  1957. SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
  1958. screen->Append( marker );
  1959. return false;
  1960. }
  1961. return true;
  1962. }
  1963. // TODO(JE) Check sheet pins here too?
  1964. bool CONNECTION_GRAPH::ercCheckNoConnects( const CONNECTION_SUBGRAPH* aSubgraph )
  1965. {
  1966. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  1967. wxString msg;
  1968. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  1969. SCH_SCREEN* screen = sheet.LastScreen();
  1970. bool ok = true;
  1971. if( aSubgraph->m_no_connect != nullptr )
  1972. {
  1973. bool has_invalid_items = false;
  1974. bool has_other_items = false;
  1975. SCH_PIN* pin = nullptr;
  1976. std::vector<SCH_ITEM*> invalid_items;
  1977. wxPoint noConnectPos = aSubgraph->m_no_connect->GetPosition();
  1978. double minDist = 0;
  1979. // Any subgraph that contains both a pin and a no-connect should not
  1980. // contain any other driving items.
  1981. for( auto item : aSubgraph->m_items )
  1982. {
  1983. switch( item->Type() )
  1984. {
  1985. case SCH_PIN_T:
  1986. {
  1987. SCH_PIN* candidate = static_cast<SCH_PIN*>( item );
  1988. double dist = VECTOR2I( candidate->GetTransformedPosition() - noConnectPos )
  1989. .SquaredEuclideanNorm();
  1990. if( !pin || dist < minDist )
  1991. {
  1992. pin = candidate;
  1993. minDist = dist;
  1994. }
  1995. has_invalid_items |= has_other_items;
  1996. has_other_items = true;
  1997. break;
  1998. }
  1999. case SCH_LINE_T:
  2000. case SCH_JUNCTION_T:
  2001. case SCH_NO_CONNECT_T:
  2002. break;
  2003. default:
  2004. has_invalid_items = true;
  2005. has_other_items = true;
  2006. invalid_items.push_back( item );
  2007. }
  2008. }
  2009. if( pin && has_invalid_items && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
  2010. {
  2011. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
  2012. ercItem->SetItems( pin );
  2013. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
  2014. screen->Append( marker );
  2015. ok = false;
  2016. }
  2017. if( !has_other_items && settings.IsTestEnabled(ERCE_NOCONNECT_NOT_CONNECTED ) )
  2018. {
  2019. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
  2020. ercItem->SetItems( aSubgraph->m_no_connect );
  2021. SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
  2022. screen->Append( marker );
  2023. ok = false;
  2024. }
  2025. }
  2026. else
  2027. {
  2028. bool has_other_connections = false;
  2029. std::vector<SCH_PIN*> pins;
  2030. // Any subgraph that lacks a no-connect and contains a pin should also
  2031. // contain at least one other potential driver
  2032. for( SCH_ITEM* item : aSubgraph->m_items )
  2033. {
  2034. switch( item->Type() )
  2035. {
  2036. case SCH_PIN_T:
  2037. {
  2038. if( !pins.empty() )
  2039. has_other_connections = true;
  2040. pins.emplace_back( static_cast<SCH_PIN*>( item ) );
  2041. break;
  2042. }
  2043. default:
  2044. if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
  2045. has_other_connections = true;
  2046. break;
  2047. }
  2048. }
  2049. // For many checks, we can just use the first pin
  2050. SCH_PIN* pin = pins.empty() ? nullptr : pins[0];
  2051. // Check if invisible power input pins connect to anything else via net name,
  2052. // but not for power symbols as the ones in the standard library all have invisible pins
  2053. // and we want to throw unconnected errors for those even if they are connected to other
  2054. // net items by name, because usually failing to connect them graphically is a mistake
  2055. if( pin && !has_other_connections
  2056. && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
  2057. && !pin->IsVisible()
  2058. && !pin->GetLibPin()->GetParent()->IsPower() )
  2059. {
  2060. wxString name = pin->Connection( &sheet )->Name();
  2061. wxString local_name = pin->Connection( &sheet )->Name( true );
  2062. if( m_global_label_cache.count( name ) ||
  2063. ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
  2064. {
  2065. has_other_connections = true;
  2066. }
  2067. }
  2068. // Only one pin, and it's not a no-connect pin
  2069. if( pin && !has_other_connections
  2070. && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
  2071. && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
  2072. && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  2073. {
  2074. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  2075. ercItem->SetItems( pin );
  2076. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
  2077. screen->Append( marker );
  2078. ok = false;
  2079. }
  2080. // If there are multiple pins in this SG, they might be indirectly connected (by netname)
  2081. // rather than directly connected (by wires). We want to flag dangling pins even if they
  2082. // join nets with another pin, as it's often a mistake
  2083. if( pins.size() > 1 )
  2084. {
  2085. for( SCH_PIN* testPin : pins )
  2086. {
  2087. // We only apply this test to power symbols, because other symbols have invisible
  2088. // pins that are meant to be dangling, but the KiCad standard library power symbols
  2089. // have invisible pins that are *not* meant to be dangling.
  2090. if( testPin->GetLibPin()->GetParent()->IsPower()
  2091. && testPin->ConnectedItems( sheet ).empty()
  2092. && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  2093. {
  2094. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  2095. ercItem->SetItems( testPin );
  2096. SCH_MARKER* marker = new SCH_MARKER( ercItem,
  2097. testPin->GetTransformedPosition() );
  2098. screen->Append( marker );
  2099. ok = false;
  2100. }
  2101. }
  2102. }
  2103. }
  2104. return ok;
  2105. }
  2106. bool CONNECTION_GRAPH::ercCheckFloatingWires( const CONNECTION_SUBGRAPH* aSubgraph )
  2107. {
  2108. if( aSubgraph->m_driver )
  2109. return true;
  2110. std::vector<SCH_ITEM*> wires;
  2111. // We've gotten this far, so we know we have no valid driver. All we need to do is check
  2112. // for a wire that we can place the error on.
  2113. for( SCH_ITEM* item : aSubgraph->m_items )
  2114. {
  2115. if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
  2116. wires.emplace_back( item );
  2117. else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
  2118. wires.emplace_back( item );
  2119. }
  2120. if( !wires.empty() )
  2121. {
  2122. SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
  2123. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
  2124. ercItem->SetItems( wires[0],
  2125. wires.size() > 1 ? wires[1] : nullptr,
  2126. wires.size() > 2 ? wires[2] : nullptr,
  2127. wires.size() > 3 ? wires[3] : nullptr );
  2128. SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
  2129. screen->Append( marker );
  2130. return false;
  2131. }
  2132. return true;
  2133. }
  2134. bool CONNECTION_GRAPH::ercCheckLabels( const CONNECTION_SUBGRAPH* aSubgraph )
  2135. {
  2136. // Label connection rules:
  2137. // Local labels are flagged if they don't connect to any pins and don't have a no-connect
  2138. // Global labels are flagged if they appear only once, don't connect to any local labels,
  2139. // and don't have a no-connect marker
  2140. // So, if there is a no-connect, we will never generate a warning here
  2141. if( aSubgraph->m_no_connect )
  2142. return true;
  2143. if( !aSubgraph->m_driver_connection )
  2144. return true;
  2145. // Buses are excluded from this test: many users create buses with only a single instance
  2146. // and it's not really a problem as long as the nets in the bus pass ERC
  2147. if( aSubgraph->m_driver_connection->IsBus() )
  2148. return true;
  2149. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  2150. bool ok = true;
  2151. SCH_TEXT* text = nullptr;
  2152. bool hasOtherConnections = false;
  2153. int pinCount = 0;
  2154. for( auto item : aSubgraph->m_items )
  2155. {
  2156. switch( item->Type() )
  2157. {
  2158. case SCH_LABEL_T:
  2159. case SCH_GLOBAL_LABEL_T:
  2160. case SCH_HIER_LABEL_T:
  2161. {
  2162. text = static_cast<SCH_TEXT*>( item );
  2163. // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
  2164. // we want to error if an individual label in the subgraph is floating, even if it's
  2165. // connected to other valid things by way of another label on the same sheet.
  2166. if( text->IsDangling() && settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
  2167. {
  2168. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
  2169. ercItem->SetItems( text );
  2170. SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
  2171. aSubgraph->m_sheet.LastScreen()->Append( marker );
  2172. ok = false;
  2173. }
  2174. break;
  2175. }
  2176. case SCH_PIN_T:
  2177. case SCH_SHEET_PIN_T:
  2178. hasOtherConnections = true;
  2179. pinCount++;
  2180. break;
  2181. default:
  2182. break;
  2183. }
  2184. }
  2185. if( !text )
  2186. return true;
  2187. bool isGlobal = text->Type() == SCH_GLOBAL_LABEL_T;
  2188. int errCode = isGlobal ? ERCE_GLOBLABEL : ERCE_LABEL_NOT_CONNECTED;
  2189. wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
  2190. wxString name = EscapeString( text->GetShownText(), CTX_NETNAME );
  2191. if( isGlobal )
  2192. {
  2193. // This will be set to true if the global is connected to a pin above, but we
  2194. // want to reset this to false so that globals get flagged if they only have a
  2195. // single instance connected to a single pin
  2196. hasOtherConnections = ( pinCount > 1 );
  2197. auto it = m_net_name_to_subgraphs_map.find( name );
  2198. if( it != m_net_name_to_subgraphs_map.end() )
  2199. {
  2200. if( it->second.size() > 1 || aSubgraph->m_multiple_drivers )
  2201. hasOtherConnections = true;
  2202. }
  2203. }
  2204. else if( text->Type() == SCH_HIER_LABEL_T )
  2205. {
  2206. // For a hier label, check if the parent pin is connected
  2207. if( aSubgraph->m_hier_parent &&
  2208. ( aSubgraph->m_hier_parent->m_strong_driver ||
  2209. aSubgraph->m_hier_parent->m_drivers.size() > 1) )
  2210. {
  2211. // For now, a simple check: if there is more than one driver, the parent is probably
  2212. // connected elsewhere (because at least one driver will be the hier pin itself)
  2213. hasOtherConnections = true;
  2214. }
  2215. }
  2216. else
  2217. {
  2218. auto pair = std::make_pair( aSubgraph->m_sheet, name );
  2219. auto it = m_local_label_cache.find( pair );
  2220. if( it != m_local_label_cache.end() && it->second.size() > 1 )
  2221. hasOtherConnections = true;
  2222. }
  2223. if( !hasOtherConnections && settings.IsTestEnabled( errCode ) )
  2224. {
  2225. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
  2226. ercItem->SetItems( text );
  2227. SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
  2228. aSubgraph->m_sheet.LastScreen()->Append( marker );
  2229. return false;
  2230. }
  2231. return ok;
  2232. }
  2233. int CONNECTION_GRAPH::ercCheckHierSheets()
  2234. {
  2235. int errors = 0;
  2236. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  2237. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  2238. {
  2239. for( SCH_ITEM* item : sheet.LastScreen()->Items() )
  2240. {
  2241. if( item->Type() != SCH_SHEET_T )
  2242. continue;
  2243. SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
  2244. std::map<wxString, SCH_SHEET_PIN*> pins;
  2245. std::map<wxString, SCH_HIERLABEL*> labels;
  2246. for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
  2247. {
  2248. pins[pin->GetText()] = pin;
  2249. if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  2250. {
  2251. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  2252. ercItem->SetItems( pin );
  2253. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
  2254. sheet.LastScreen()->Append( marker );
  2255. errors++;
  2256. }
  2257. }
  2258. std::set<wxString> matchedPins;
  2259. for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
  2260. {
  2261. if( subItem->Type() == SCH_HIER_LABEL_T )
  2262. {
  2263. SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
  2264. if( !pins.count( label->GetText() ) )
  2265. labels[label->GetText()] = label;
  2266. else
  2267. matchedPins.insert( label->GetText() );
  2268. }
  2269. }
  2270. for( const wxString& matched : matchedPins )
  2271. pins.erase( matched );
  2272. for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
  2273. {
  2274. wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
  2275. "label inside the sheet" ),
  2276. UnescapeString( unmatched.first ) );
  2277. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
  2278. ercItem->SetItems( unmatched.second );
  2279. ercItem->SetErrorMessage( msg );
  2280. ercItem->SetIsSheetSpecific();
  2281. SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
  2282. sheet.LastScreen()->Append( marker );
  2283. errors++;
  2284. }
  2285. for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
  2286. {
  2287. wxString msg = wxString::Format( _( "Hierarchical label %s has no matching "
  2288. "sheet pin in the parent sheet" ),
  2289. UnescapeString( unmatched.first ) );
  2290. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
  2291. ercItem->SetItems( unmatched.second );
  2292. ercItem->SetErrorMessage( msg );
  2293. ercItem->SetIsSheetSpecific();
  2294. SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
  2295. parentSheet->GetScreen()->Append( marker );
  2296. errors++;
  2297. }
  2298. }
  2299. }
  2300. return errors;
  2301. }