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.

3287 lines
111 KiB

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