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.

3200 lines
108 KiB

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