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.

4257 lines
144 KiB

3 years ago
3 years ago
2 years ago
2 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
5 years ago
2 years ago
2 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
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
2 years ago
4 years ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
4 years ago
1 year ago
1 year 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-2024 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 <core/profile.h>
  27. #include <core/kicad_algo.h>
  28. #include <common.h>
  29. #include <core/kicad_algo.h>
  30. #include <erc/erc.h>
  31. #include <pin_type.h>
  32. #include <sch_bus_entry.h>
  33. #include <sch_symbol.h>
  34. #include <sch_edit_frame.h>
  35. #include <sch_line.h>
  36. #include <sch_marker.h>
  37. #include <sch_pin.h>
  38. #include <sch_rule_area.h>
  39. #include <sch_sheet.h>
  40. #include <sch_sheet_path.h>
  41. #include <sch_sheet_pin.h>
  42. #include <sch_text.h>
  43. #include <schematic.h>
  44. #include <connection_graph.h>
  45. #include <project/project_file.h>
  46. #include <project/net_settings.h>
  47. #include <widgets/ui_common.h>
  48. #include <string_utils.h>
  49. #include <core/thread_pool.h>
  50. #include <wx/log.h>
  51. #include <advanced_config.h> // for realtime connectivity switch in release builds
  52. /**
  53. * Flag to enable connectivity profiling
  54. * @ingroup trace_env_vars
  55. */
  56. static const wxChar DanglingProfileMask[] = wxT( "CONN_PROFILE" );
  57. /**
  58. * Flag to enable connectivity tracing
  59. * @ingroup trace_env_vars
  60. */
  61. static const wxChar ConnTrace[] = wxT( "CONN" );
  62. void CONNECTION_SUBGRAPH::RemoveItem( SCH_ITEM* aItem )
  63. {
  64. m_items.erase( aItem );
  65. m_drivers.erase( aItem );
  66. if( aItem == m_driver )
  67. {
  68. m_driver = nullptr;
  69. m_driver_connection = nullptr;
  70. }
  71. if( aItem->Type() == SCH_SHEET_PIN_T )
  72. m_hier_pins.erase( static_cast<SCH_SHEET_PIN*>( aItem ) );
  73. if( aItem->Type() == SCH_HIER_LABEL_T )
  74. m_hier_ports.erase( static_cast<SCH_HIERLABEL*>( aItem ) );
  75. }
  76. void CONNECTION_SUBGRAPH::ExchangeItem( SCH_ITEM* aOldItem, SCH_ITEM* aNewItem )
  77. {
  78. m_items.erase( aOldItem );
  79. m_items.insert( aNewItem );
  80. m_drivers.erase( aOldItem );
  81. m_drivers.insert( aNewItem );
  82. if( aOldItem == m_driver )
  83. {
  84. m_driver = aNewItem;
  85. m_driver_connection = aNewItem->GetOrInitConnection( m_sheet, m_graph );
  86. }
  87. SCH_CONNECTION* old_conn = aOldItem->Connection( &m_sheet );
  88. SCH_CONNECTION* new_conn = aNewItem->GetOrInitConnection( m_sheet, m_graph );
  89. if( old_conn && new_conn )
  90. {
  91. new_conn->Clone( *old_conn );
  92. if( old_conn->IsDriver() )
  93. new_conn->SetDriver( aNewItem );
  94. new_conn->ClearDirty();
  95. }
  96. if( aOldItem->Type() == SCH_SHEET_PIN_T )
  97. {
  98. m_hier_pins.erase( static_cast<SCH_SHEET_PIN*>( aOldItem ) );
  99. m_hier_pins.insert( static_cast<SCH_SHEET_PIN*>( aNewItem ) );
  100. }
  101. if( aOldItem->Type() == SCH_HIER_LABEL_T )
  102. {
  103. m_hier_ports.erase( static_cast<SCH_HIERLABEL*>( aOldItem ) );
  104. m_hier_ports.insert( static_cast<SCH_HIERLABEL*>( aNewItem ) );
  105. }
  106. }
  107. bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers )
  108. {
  109. std::lock_guard lock( m_driver_mutex );
  110. auto candidate_cmp = [&]( SCH_ITEM* a, SCH_ITEM* b ) -> bool
  111. {
  112. // meet irreflexive requirements of std::sort
  113. if( a == b )
  114. return false;
  115. SCH_CONNECTION* ac = a->Connection( &m_sheet );
  116. SCH_CONNECTION* bc = b->Connection( &m_sheet );
  117. // Ensure we don't pick the subset over the superset
  118. if( ac->IsBus() && bc->IsBus() )
  119. return bc->IsSubsetOf( ac );
  120. // Ensure we don't pick a hidden power pin on a regular symbol over
  121. // one on a power symbol
  122. if( a->Type() == SCH_PIN_T && b->Type() == SCH_PIN_T )
  123. {
  124. SCH_PIN* pa = static_cast<SCH_PIN*>( a );
  125. SCH_PIN* pb = static_cast<SCH_PIN*>( b );
  126. bool aPower = pa->GetLibPin()->GetParentSymbol()->IsPower();
  127. bool bPower = pb->GetLibPin()->GetParentSymbol()->IsPower();
  128. if( aPower && !bPower )
  129. return true;
  130. else if( bPower && !aPower )
  131. return false;
  132. }
  133. const wxString& a_name = GetNameForDriver( a );
  134. const wxString& b_name = GetNameForDriver( b );
  135. bool a_lowQualityName = a_name.Contains( "-Pad" );
  136. bool b_lowQualityName = b_name.Contains( "-Pad" );
  137. if( a_lowQualityName && !b_lowQualityName )
  138. return false;
  139. else if( b_lowQualityName && !a_lowQualityName )
  140. return true;
  141. else
  142. return a_name < b_name;
  143. };
  144. PRIORITY highest_priority = PRIORITY::INVALID;
  145. std::set<SCH_ITEM*, decltype( candidate_cmp )> candidates( candidate_cmp );
  146. std::set<SCH_ITEM*> strong_drivers;
  147. m_driver = nullptr;
  148. // Hierarchical labels are lower priority than local labels here,
  149. // because on the first pass we want local labels to drive subgraphs
  150. // so that we can identify same-sheet neighbors and link them together.
  151. // Hierarchical labels will end up overriding the final net name if
  152. // a higher-level sheet has a different name during the hierarchical
  153. // pass.
  154. for( SCH_ITEM* item : m_drivers )
  155. {
  156. PRIORITY item_priority = GetDriverPriority( item );
  157. if( item_priority == PRIORITY::PIN )
  158. {
  159. SCH_PIN* pin = static_cast<SCH_PIN*>( item );
  160. if( !static_cast<SCH_SYMBOL*>( pin->GetParentSymbol() )->IsInNetlist() )
  161. continue;
  162. }
  163. if( item_priority >= PRIORITY::HIER_LABEL )
  164. strong_drivers.insert( item );
  165. if( item_priority > highest_priority )
  166. {
  167. candidates.clear();
  168. candidates.insert( item );
  169. highest_priority = item_priority;
  170. }
  171. else if( !candidates.empty() && ( item_priority == highest_priority ) )
  172. {
  173. candidates.insert( item );
  174. }
  175. }
  176. if( highest_priority >= PRIORITY::HIER_LABEL )
  177. m_strong_driver = true;
  178. // Power pins are 5, global labels are 6
  179. m_local_driver = ( highest_priority < PRIORITY::POWER_PIN );
  180. if( !candidates.empty() )
  181. {
  182. if( candidates.size() > 1 )
  183. {
  184. if( highest_priority == PRIORITY::SHEET_PIN )
  185. {
  186. // We have multiple options, and they are all hierarchical
  187. // sheet pins. Let's prefer outputs over inputs.
  188. for( SCH_ITEM* c : candidates )
  189. {
  190. SCH_SHEET_PIN* p = static_cast<SCH_SHEET_PIN*>( c );
  191. if( p->GetShape() == LABEL_FLAG_SHAPE::L_OUTPUT )
  192. {
  193. m_driver = c;
  194. break;
  195. }
  196. }
  197. }
  198. }
  199. if( !m_driver )
  200. m_driver = *candidates.begin();
  201. }
  202. if( strong_drivers.size() > 1 )
  203. m_multiple_drivers = true;
  204. // Drop weak drivers
  205. if( m_strong_driver )
  206. {
  207. m_drivers.clear();
  208. m_drivers.insert( strong_drivers.begin(), strong_drivers.end() );
  209. }
  210. // Cache driver connection
  211. if( m_driver )
  212. {
  213. m_driver_connection = m_driver->Connection( &m_sheet );
  214. m_driver_connection->ConfigureFromLabel( GetNameForDriver( m_driver ) );
  215. m_driver_connection->SetDriver( m_driver );
  216. m_driver_connection->ClearDirty();
  217. }
  218. else if( !m_is_bus_member )
  219. {
  220. m_driver_connection = nullptr;
  221. }
  222. return ( m_driver != nullptr );
  223. }
  224. void CONNECTION_SUBGRAPH::getAllConnectedItems( std::set<std::pair<SCH_SHEET_PATH,
  225. SCH_ITEM*>>& aItems,
  226. std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
  227. {
  228. CONNECTION_SUBGRAPH* sg = this;
  229. while( sg->m_absorbed_by )
  230. {
  231. wxASSERT( sg->m_graph == sg->m_absorbed_by->m_graph );
  232. sg = sg->m_absorbed_by;
  233. }
  234. // If we are unable to insert the subgraph into the set, then we have already
  235. // visited it and don't need to add it again.
  236. if( aSubgraphs.insert( sg ).second == false )
  237. return;
  238. aSubgraphs.insert( sg->m_absorbed_subgraphs.begin(), sg->m_absorbed_subgraphs.end() );
  239. for( SCH_ITEM* item : sg->m_items )
  240. aItems.emplace( m_sheet, item );
  241. for( CONNECTION_SUBGRAPH* child_sg : sg->m_hier_children )
  242. child_sg->getAllConnectedItems( aItems, aSubgraphs );
  243. }
  244. wxString CONNECTION_SUBGRAPH::GetNetName() const
  245. {
  246. if( !m_driver || m_dirty )
  247. return "";
  248. if( !m_driver->Connection( &m_sheet ) )
  249. {
  250. #ifdef CONNECTIVITY_DEBUG
  251. wxASSERT_MSG( false, wxS( "Tried to get the net name of an item with no connection" ) );
  252. #endif
  253. return "";
  254. }
  255. return m_driver->Connection( &m_sheet )->Name();
  256. }
  257. std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetAllBusLabels() const
  258. {
  259. std::vector<SCH_ITEM*> labels;
  260. for( SCH_ITEM* item : m_drivers )
  261. {
  262. switch( item->Type() )
  263. {
  264. case SCH_LABEL_T:
  265. case SCH_GLOBAL_LABEL_T:
  266. {
  267. CONNECTION_TYPE type = item->Connection( &m_sheet )->Type();
  268. // Only consider bus vectors
  269. if( type == CONNECTION_TYPE::BUS || type == CONNECTION_TYPE::BUS_GROUP )
  270. labels.push_back( item );
  271. break;
  272. }
  273. default:
  274. break;
  275. }
  276. }
  277. return labels;
  278. }
  279. std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetVectorBusLabels() const
  280. {
  281. std::vector<SCH_ITEM*> labels;
  282. for( SCH_ITEM* item : m_drivers )
  283. {
  284. switch( item->Type() )
  285. {
  286. case SCH_LABEL_T:
  287. case SCH_GLOBAL_LABEL_T:
  288. {
  289. SCH_CONNECTION* label_conn = item->Connection( &m_sheet );
  290. // Only consider bus vectors
  291. if( label_conn->Type() == CONNECTION_TYPE::BUS )
  292. labels.push_back( item );
  293. break;
  294. }
  295. default:
  296. break;
  297. }
  298. }
  299. return labels;
  300. }
  301. wxString CONNECTION_SUBGRAPH::driverName( SCH_ITEM* aItem ) const
  302. {
  303. switch( aItem->Type() )
  304. {
  305. case SCH_PIN_T:
  306. {
  307. SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
  308. bool forceNoConnect = m_no_connect != nullptr;
  309. return pin->GetDefaultNetName( m_sheet, forceNoConnect );
  310. }
  311. case SCH_LABEL_T:
  312. case SCH_GLOBAL_LABEL_T:
  313. case SCH_HIER_LABEL_T:
  314. {
  315. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
  316. // NB: any changes here will need corresponding changes in SCH_LABEL_BASE::cacheShownText()
  317. return EscapeString( label->GetShownText( &m_sheet, false ), CTX_NETNAME );
  318. }
  319. case SCH_SHEET_PIN_T:
  320. {
  321. // Sheet pins need to use their parent sheet as their starting sheet or they will resolve
  322. // variables on the current sheet first
  323. SCH_SHEET_PIN* sheetPin = static_cast<SCH_SHEET_PIN*>( aItem );
  324. SCH_SHEET_PATH path = m_sheet;
  325. if( path.Last() != sheetPin->GetParent() )
  326. path.push_back( sheetPin->GetParent() );
  327. return EscapeString( sheetPin->GetShownText( &path, false ), CTX_NETNAME );
  328. }
  329. default:
  330. wxFAIL_MSG( wxS( "Unhandled item type in GetNameForDriver" ) );
  331. return wxEmptyString;
  332. }
  333. }
  334. const wxString& CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem ) const
  335. {
  336. if( aItem->HasCachedDriverName() )
  337. return aItem->GetCachedDriverName();
  338. auto it = m_driver_name_cache.find( aItem );
  339. if( it != m_driver_name_cache.end() )
  340. return it->second;
  341. return m_driver_name_cache.emplace( aItem, driverName( aItem ) ).first->second;
  342. }
  343. const std::vector<std::pair<wxString, SCH_ITEM*>>
  344. CONNECTION_SUBGRAPH::GetNetclassesForDriver( SCH_ITEM* aItem, bool returnAll ) const
  345. {
  346. std::vector<std::pair<wxString, SCH_ITEM*>> foundNetclasses;
  347. const std::unordered_set<SCH_RULE_AREA*>& ruleAreaCache = aItem->GetRuleAreaCache();
  348. // Get netclasses on attached rule areas
  349. for( SCH_RULE_AREA* ruleArea : ruleAreaCache )
  350. {
  351. const std::vector<std::pair<wxString, SCH_ITEM*>> ruleNetclasses =
  352. ruleArea->GetResolvedNetclasses();
  353. if( ruleNetclasses.size() > 0 )
  354. {
  355. if( returnAll )
  356. {
  357. foundNetclasses.insert( foundNetclasses.end(), ruleNetclasses.begin(),
  358. ruleNetclasses.end() );
  359. }
  360. else
  361. {
  362. foundNetclasses.push_back( ruleNetclasses[0] );
  363. return foundNetclasses;
  364. }
  365. }
  366. }
  367. // Get netclasses on child fields
  368. aItem->RunOnChildren(
  369. [&]( SCH_ITEM* aChild )
  370. {
  371. if( aChild->Type() == SCH_FIELD_T )
  372. {
  373. SCH_FIELD* field = static_cast<SCH_FIELD*>( aChild );
  374. if( field->GetCanonicalName() == wxT( "Netclass" ) )
  375. {
  376. wxString netclass = field->GetShownText( &m_sheet, false );
  377. if( netclass != wxEmptyString )
  378. foundNetclasses.push_back( { netclass, aItem } );
  379. return returnAll;
  380. }
  381. }
  382. return true;
  383. } );
  384. return foundNetclasses;
  385. }
  386. void CONNECTION_SUBGRAPH::Absorb( CONNECTION_SUBGRAPH* aOther )
  387. {
  388. wxASSERT( m_sheet == aOther->m_sheet );
  389. for( SCH_ITEM* item : aOther->m_items )
  390. {
  391. item->Connection( &m_sheet )->SetSubgraphCode( m_code );
  392. AddItem( item );
  393. }
  394. m_absorbed_subgraphs.insert( aOther );
  395. m_absorbed_subgraphs.insert( aOther->m_absorbed_subgraphs.begin(),
  396. aOther->m_absorbed_subgraphs.end() );
  397. m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
  398. m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
  399. m_multiple_drivers |= aOther->m_multiple_drivers;
  400. std::function<void( CONNECTION_SUBGRAPH* )> set_absorbed_by =
  401. [ & ]( CONNECTION_SUBGRAPH *child )
  402. {
  403. child->m_absorbed_by = this;
  404. for( CONNECTION_SUBGRAPH* subchild : child->m_absorbed_subgraphs )
  405. set_absorbed_by( subchild );
  406. };
  407. aOther->m_absorbed = true;
  408. aOther->m_dirty = false;
  409. aOther->m_driver = nullptr;
  410. aOther->m_driver_connection = nullptr;
  411. set_absorbed_by( aOther );
  412. }
  413. void CONNECTION_SUBGRAPH::AddItem( SCH_ITEM* aItem )
  414. {
  415. m_items.insert( aItem );
  416. if( aItem->Connection( &m_sheet )->IsDriver() )
  417. m_drivers.insert( aItem );
  418. if( aItem->Type() == SCH_SHEET_PIN_T )
  419. m_hier_pins.insert( static_cast<SCH_SHEET_PIN*>( aItem ) );
  420. else if( aItem->Type() == SCH_HIER_LABEL_T )
  421. m_hier_ports.insert( static_cast<SCH_HIERLABEL*>( aItem ) );
  422. }
  423. void CONNECTION_SUBGRAPH::UpdateItemConnections()
  424. {
  425. if( !m_driver_connection )
  426. return;
  427. for( SCH_ITEM* item : m_items )
  428. {
  429. SCH_CONNECTION* item_conn = item->GetOrInitConnection( m_sheet, m_graph );
  430. if( !item_conn )
  431. continue;
  432. if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
  433. ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
  434. {
  435. continue;
  436. }
  437. if( item != m_driver )
  438. {
  439. item_conn->Clone( *m_driver_connection );
  440. item_conn->ClearDirty();
  441. }
  442. }
  443. }
  444. CONNECTION_SUBGRAPH::PRIORITY CONNECTION_SUBGRAPH::GetDriverPriority( SCH_ITEM* aDriver )
  445. {
  446. if( !aDriver )
  447. return PRIORITY::NONE;
  448. switch( aDriver->Type() )
  449. {
  450. case SCH_SHEET_PIN_T: return PRIORITY::SHEET_PIN;
  451. case SCH_HIER_LABEL_T: return PRIORITY::HIER_LABEL;
  452. case SCH_LABEL_T: return PRIORITY::LOCAL_LABEL;
  453. case SCH_GLOBAL_LABEL_T: return PRIORITY::GLOBAL;
  454. case SCH_PIN_T:
  455. {
  456. SCH_PIN* sch_pin = static_cast<SCH_PIN*>( aDriver );
  457. const SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( sch_pin->GetParentSymbol() );
  458. if( sch_pin->IsGlobalPower() )
  459. return PRIORITY::POWER_PIN;
  460. else if( !sym || sym->GetExcludedFromBoard()
  461. || sym->GetLibSymbolRef()->GetReferenceField().GetText().StartsWith( '#' ) )
  462. return PRIORITY::NONE;
  463. else
  464. return PRIORITY::PIN;
  465. }
  466. default: return PRIORITY::NONE;
  467. }
  468. }
  469. void CONNECTION_GRAPH::Merge( CONNECTION_GRAPH& aGraph )
  470. {
  471. std::copy( aGraph.m_items.begin(), aGraph.m_items.end(),
  472. std::back_inserter( m_items ) );
  473. for( SCH_ITEM* item : aGraph.m_items )
  474. item->SetConnectionGraph( this );
  475. std::copy( aGraph.m_subgraphs.begin(), aGraph.m_subgraphs.end(),
  476. std::back_inserter( m_subgraphs ) );
  477. for( CONNECTION_SUBGRAPH* sg : aGraph.m_subgraphs )
  478. {
  479. if( sg->m_driver_connection )
  480. sg->m_driver_connection->SetGraph( this );
  481. sg->m_graph = this;
  482. }
  483. std::copy( aGraph.m_driver_subgraphs.begin(),
  484. aGraph.m_driver_subgraphs.end(),
  485. std::back_inserter( m_driver_subgraphs ) );
  486. std::copy( aGraph.m_global_power_pins.begin(),
  487. aGraph.m_global_power_pins.end(),
  488. std::back_inserter( m_global_power_pins ) );
  489. for( auto& [key, value] : aGraph.m_net_name_to_subgraphs_map )
  490. m_net_name_to_subgraphs_map.insert_or_assign( key, value );
  491. for( auto& [key, value] : aGraph.m_sheet_to_subgraphs_map )
  492. m_sheet_to_subgraphs_map.insert_or_assign( key, value );
  493. for( auto& [key, value] : aGraph.m_net_name_to_code_map )
  494. m_net_name_to_code_map.insert_or_assign( key, value );
  495. for( auto& [key, value] : aGraph.m_bus_name_to_code_map )
  496. m_bus_name_to_code_map.insert_or_assign( key, value );
  497. for( auto& [key, value] : aGraph.m_net_code_to_subgraphs_map )
  498. m_net_code_to_subgraphs_map.insert_or_assign( key, value );
  499. for( auto& [key, value] : aGraph.m_item_to_subgraph_map )
  500. m_item_to_subgraph_map.insert_or_assign( key, value );
  501. for( auto& [key, value] : aGraph.m_local_label_cache )
  502. m_local_label_cache.insert_or_assign( key, value );
  503. for( auto& [key, value] : aGraph.m_global_label_cache )
  504. m_global_label_cache.insert_or_assign( key, value );
  505. m_last_bus_code = std::max( m_last_bus_code, aGraph.m_last_bus_code );
  506. m_last_net_code = std::max( m_last_net_code, aGraph.m_last_net_code );
  507. m_last_subgraph_code = std::max( m_last_subgraph_code, aGraph.m_last_subgraph_code );
  508. }
  509. void CONNECTION_GRAPH::ExchangeItem( SCH_ITEM* aOldItem, SCH_ITEM* aNewItem )
  510. {
  511. wxCHECK2( aOldItem->Type() == aNewItem->Type(), return );
  512. auto exchange = [&]( SCH_ITEM* aOld, SCH_ITEM* aNew )
  513. {
  514. auto it = m_item_to_subgraph_map.find( aOld );
  515. if( it == m_item_to_subgraph_map.end() )
  516. return;
  517. CONNECTION_SUBGRAPH* sg = it->second;
  518. sg->ExchangeItem( aOld, aNew );
  519. m_item_to_subgraph_map.erase( it );
  520. m_item_to_subgraph_map.emplace( aNew, sg );
  521. for( auto it2 = m_items.begin(); it2 != m_items.end(); ++it2 )
  522. {
  523. if( *it2 == aOld )
  524. {
  525. *it2 = aNew;
  526. break;
  527. }
  528. }
  529. };
  530. exchange( aOldItem, aNewItem );
  531. if( aOldItem->Type() == SCH_SYMBOL_T )
  532. {
  533. SCH_SYMBOL* oldSymbol = static_cast<SCH_SYMBOL*>( aOldItem );
  534. SCH_SYMBOL* newSymbol = static_cast<SCH_SYMBOL*>( aNewItem );
  535. std::vector<SCH_PIN*> oldPins = oldSymbol->GetPins( &m_schematic->CurrentSheet() );
  536. std::vector<SCH_PIN*> newPins = newSymbol->GetPins( &m_schematic->CurrentSheet() );
  537. wxCHECK2( oldPins.size() == newPins.size(), return );
  538. for( size_t ii = 0; ii < oldPins.size(); ii++ )
  539. {
  540. exchange( oldPins[ii], newPins[ii] );
  541. }
  542. }
  543. }
  544. void CONNECTION_GRAPH::Reset()
  545. {
  546. for( auto& subgraph : m_subgraphs )
  547. {
  548. /// Only delete subgraphs of which we are the owner
  549. if( subgraph->m_graph == this )
  550. delete subgraph;
  551. }
  552. m_items.clear();
  553. m_subgraphs.clear();
  554. m_driver_subgraphs.clear();
  555. m_sheet_to_subgraphs_map.clear();
  556. m_global_power_pins.clear();
  557. m_bus_alias_cache.clear();
  558. m_net_name_to_code_map.clear();
  559. m_bus_name_to_code_map.clear();
  560. m_net_code_to_subgraphs_map.clear();
  561. m_net_name_to_subgraphs_map.clear();
  562. m_item_to_subgraph_map.clear();
  563. m_local_label_cache.clear();
  564. m_global_label_cache.clear();
  565. m_last_net_code = 1;
  566. m_last_bus_code = 1;
  567. m_last_subgraph_code = 1;
  568. }
  569. void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional,
  570. std::function<void( SCH_ITEM* )>* aChangedItemHandler )
  571. {
  572. PROF_TIMER recalc_time( "CONNECTION_GRAPH::Recalculate" );
  573. if( aUnconditional )
  574. Reset();
  575. PROF_TIMER update_items( "updateItemConnectivity" );
  576. m_sheetList = aSheetList;
  577. std::set<SCH_ITEM*> dirty_items;
  578. for( const SCH_SHEET_PATH& sheet : aSheetList )
  579. {
  580. std::vector<SCH_ITEM*> items;
  581. // Store current unit value, to replace it after calculations
  582. std::vector<std::pair<SCH_SYMBOL*, int>> symbolsChanged;
  583. for( SCH_ITEM* item : sheet.LastScreen()->Items() )
  584. {
  585. if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
  586. {
  587. wxLogTrace( ConnTrace, wxT( "Adding item %s to connectivity graph update" ),
  588. item->GetTypeDesc() );
  589. items.push_back( item );
  590. dirty_items.insert( item );
  591. // Add any symbol dirty pins to the dirty_items list
  592. if( item->Type() == SCH_SYMBOL_T )
  593. {
  594. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  595. for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
  596. {
  597. if( pin->IsConnectivityDirty() )
  598. {
  599. dirty_items.insert( pin );
  600. }
  601. }
  602. }
  603. }
  604. // If the symbol isn't dirty, look at the pins
  605. // TODO: remove symbols from connectivity graph and only use pins
  606. else if( item->Type() == SCH_SYMBOL_T )
  607. {
  608. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  609. for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
  610. {
  611. if( pin->IsConnectivityDirty() )
  612. {
  613. items.push_back( pin );
  614. dirty_items.insert( pin );
  615. }
  616. }
  617. }
  618. else if( item->Type() == SCH_SHEET_T )
  619. {
  620. SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
  621. for( SCH_SHEET_PIN* pin : sheetItem->GetPins() )
  622. {
  623. if( pin->IsConnectivityDirty() )
  624. {
  625. items.push_back( pin );
  626. dirty_items.insert( pin );
  627. }
  628. }
  629. }
  630. // Ensure the hierarchy info stored in the SCH_SCREEN (such as symbol units) reflects
  631. // the current SCH_SHEET_PATH
  632. if( item->Type() == SCH_SYMBOL_T )
  633. {
  634. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  635. int new_unit = symbol->GetUnitSelection( &sheet );
  636. // Store the initial unit value so we can restore it after calculations
  637. if( symbol->GetUnit() != new_unit )
  638. symbolsChanged.push_back( { symbol, symbol->GetUnit() } );
  639. symbol->SetUnit( new_unit );
  640. }
  641. }
  642. m_items.reserve( m_items.size() + items.size() );
  643. updateItemConnectivity( sheet, items );
  644. // UpdateDanglingState() also adds connected items for SCH_TEXT
  645. sheet.LastScreen()->TestDanglingEnds( &sheet, aChangedItemHandler );
  646. // Restore the m_unit member variables where we had to change them
  647. for( const auto& [ symbol, originalUnit ] : symbolsChanged )
  648. symbol->SetUnit( originalUnit );
  649. }
  650. // Restore the danlging states of items in the current SCH_SCREEN to match the current
  651. // SCH_SHEET_PATH.
  652. m_schematic->CurrentSheet().LastScreen()->TestDanglingEnds( &m_schematic->CurrentSheet(),
  653. aChangedItemHandler );
  654. for( SCH_ITEM* item : dirty_items )
  655. item->SetConnectivityDirty( false );
  656. if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
  657. update_items.Show();
  658. PROF_TIMER build_graph( "buildConnectionGraph" );
  659. buildConnectionGraph( aChangedItemHandler, aUnconditional );
  660. if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
  661. build_graph.Show();
  662. recalc_time.Stop();
  663. if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
  664. recalc_time.Show();
  665. }
  666. std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> CONNECTION_GRAPH::ExtractAffectedItems(
  667. const std::set<SCH_ITEM*> &aItems )
  668. {
  669. std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> retvals;
  670. std::set<CONNECTION_SUBGRAPH*> subgraphs;
  671. auto traverse_subgraph = [&retvals, &subgraphs]( CONNECTION_SUBGRAPH* aSubgraph )
  672. {
  673. // Find the primary subgraph on this sheet
  674. while( aSubgraph->m_absorbed_by )
  675. {
  676. wxASSERT( aSubgraph->m_graph == aSubgraph->m_absorbed_by->m_graph );
  677. aSubgraph = aSubgraph->m_absorbed_by;
  678. }
  679. // Find the top most connected subgraph on all sheets
  680. while( aSubgraph->m_hier_parent )
  681. {
  682. wxASSERT( aSubgraph->m_graph == aSubgraph->m_hier_parent->m_graph );
  683. aSubgraph = aSubgraph->m_hier_parent;
  684. }
  685. // Recurse through all subsheets to collect connected items
  686. aSubgraph->getAllConnectedItems( retvals, subgraphs );
  687. };
  688. auto extract_element = [&]( SCH_ITEM* aItem )
  689. {
  690. CONNECTION_SUBGRAPH* item_sg = GetSubgraphForItem( aItem );
  691. if( !item_sg )
  692. {
  693. wxLogTrace( ConnTrace, wxT( "Item %s not found in connection graph" ), aItem->GetTypeDesc() );
  694. return;
  695. }
  696. if( !item_sg->ResolveDrivers( true ) )
  697. {
  698. wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld (%p) has no driver" ),
  699. aItem->GetTypeDesc(), item_sg->m_code, item_sg );
  700. }
  701. std::vector<CONNECTION_SUBGRAPH*> sg_to_scan = GetAllSubgraphs( item_sg->GetNetName() );
  702. if( sg_to_scan.empty() )
  703. {
  704. wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld with net %s has no neighbors" ),
  705. aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName() );
  706. sg_to_scan.push_back( item_sg );
  707. }
  708. wxLogTrace( ConnTrace,
  709. wxT( "Removing all item %s connections from subgraph %ld with net %s: Found "
  710. "%zu subgraphs" ),
  711. aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName(),
  712. sg_to_scan.size() );
  713. for( CONNECTION_SUBGRAPH* sg : sg_to_scan )
  714. {
  715. traverse_subgraph( sg );
  716. for( auto& bus_it : sg->m_bus_neighbors )
  717. {
  718. for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
  719. traverse_subgraph( bus_sg );
  720. }
  721. for( auto& bus_it : sg->m_bus_parents )
  722. {
  723. for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
  724. traverse_subgraph( bus_sg );
  725. }
  726. }
  727. alg::delete_matching( m_items, aItem );
  728. };
  729. for( SCH_ITEM* item : aItems )
  730. {
  731. if( item->Type() == SCH_SHEET_T )
  732. {
  733. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  734. for( SCH_SHEET_PIN* pin : sheet->GetPins() )
  735. extract_element( pin );
  736. }
  737. else if ( item->Type() == SCH_SYMBOL_T )
  738. {
  739. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  740. for( SCH_PIN* pin : symbol->GetPins( &m_schematic->CurrentSheet() ) )
  741. extract_element( pin );
  742. }
  743. else
  744. {
  745. extract_element( item );
  746. }
  747. }
  748. removeSubgraphs( subgraphs );
  749. for( const auto& [path, item] : retvals )
  750. alg::delete_matching( m_items, item );
  751. return retvals;
  752. }
  753. void CONNECTION_GRAPH::RemoveItem( SCH_ITEM* aItem )
  754. {
  755. auto it = m_item_to_subgraph_map.find( aItem );
  756. if( it == m_item_to_subgraph_map.end() )
  757. return;
  758. CONNECTION_SUBGRAPH* subgraph = it->second;
  759. while(subgraph->m_absorbed_by )
  760. subgraph = subgraph->m_absorbed_by;
  761. subgraph->RemoveItem( aItem );
  762. alg::delete_matching( m_items, aItem );
  763. m_item_to_subgraph_map.erase( it );
  764. }
  765. void CONNECTION_GRAPH::removeSubgraphs( std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
  766. {
  767. wxLogTrace( ConnTrace, wxT( "Removing %zu subgraphs" ), aSubgraphs.size() );
  768. std::sort( m_driver_subgraphs.begin(), m_driver_subgraphs.end() );
  769. std::sort( m_subgraphs.begin(), m_subgraphs.end() );
  770. std::set<int> codes_to_remove;
  771. for( auto& el : m_sheet_to_subgraphs_map )
  772. {
  773. std::sort( el.second.begin(), el.second.end() );
  774. }
  775. for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
  776. {
  777. for( auto& it : sg->m_bus_neighbors )
  778. {
  779. for( CONNECTION_SUBGRAPH* neighbor : it.second )
  780. {
  781. auto& parents = neighbor->m_bus_parents[it.first];
  782. for( auto test = parents.begin(); test != parents.end(); )
  783. {
  784. if( *test == sg )
  785. test = parents.erase( test );
  786. else
  787. ++test;
  788. }
  789. if( parents.empty() )
  790. neighbor->m_bus_parents.erase( it.first );
  791. }
  792. }
  793. for( auto& it : sg->m_bus_parents )
  794. {
  795. for( CONNECTION_SUBGRAPH* parent : it.second )
  796. {
  797. auto& neighbors = parent->m_bus_neighbors[it.first];
  798. for( auto test = neighbors.begin(); test != neighbors.end(); )
  799. {
  800. if( *test == sg )
  801. test = neighbors.erase( test );
  802. else
  803. ++test;
  804. }
  805. if( neighbors.empty() )
  806. parent->m_bus_neighbors.erase( it.first );
  807. }
  808. }
  809. {
  810. auto it = std::lower_bound( m_driver_subgraphs.begin(), m_driver_subgraphs.end(), sg );
  811. while( it != m_driver_subgraphs.end() && *it == sg )
  812. it = m_driver_subgraphs.erase( it );
  813. }
  814. {
  815. auto it = std::lower_bound( m_subgraphs.begin(), m_subgraphs.end(), sg );
  816. while( it != m_subgraphs.end() && *it == sg )
  817. it = m_subgraphs.erase( it );
  818. }
  819. for( auto& el : m_sheet_to_subgraphs_map )
  820. {
  821. auto it = std::lower_bound( el.second.begin(), el.second.end(), sg );
  822. while( it != el.second.end() && *it == sg )
  823. it = el.second.erase( it );
  824. }
  825. auto remove_sg = [sg]( auto it ) -> bool
  826. {
  827. for( const CONNECTION_SUBGRAPH* test_sg : it->second )
  828. {
  829. if( sg == test_sg )
  830. return true;
  831. }
  832. return false;
  833. };
  834. for( auto it = m_global_label_cache.begin(); it != m_global_label_cache.end(); )
  835. {
  836. if( remove_sg( it ) )
  837. it = m_global_label_cache.erase( it );
  838. else
  839. ++it;
  840. }
  841. for( auto it = m_local_label_cache.begin(); it != m_local_label_cache.end(); )
  842. {
  843. if( remove_sg( it ) )
  844. it = m_local_label_cache.erase( it );
  845. else
  846. ++it;
  847. }
  848. for( auto it = m_net_code_to_subgraphs_map.begin();
  849. it != m_net_code_to_subgraphs_map.end(); )
  850. {
  851. if( remove_sg( it ) )
  852. {
  853. codes_to_remove.insert( it->first.Netcode );
  854. it = m_net_code_to_subgraphs_map.erase( it );
  855. }
  856. else
  857. ++it;
  858. }
  859. for( auto it = m_net_name_to_subgraphs_map.begin();
  860. it != m_net_name_to_subgraphs_map.end(); )
  861. {
  862. if( remove_sg( it ) )
  863. it = m_net_name_to_subgraphs_map.erase( it );
  864. else
  865. ++it;
  866. }
  867. for( auto it = m_item_to_subgraph_map.begin(); it != m_item_to_subgraph_map.end(); )
  868. {
  869. if( it->second == sg )
  870. it = m_item_to_subgraph_map.erase( it );
  871. else
  872. ++it;
  873. }
  874. }
  875. for( auto it = m_net_name_to_code_map.begin(); it != m_net_name_to_code_map.end(); )
  876. {
  877. if( codes_to_remove.contains( it->second ) )
  878. it = m_net_name_to_code_map.erase( it );
  879. else
  880. ++it;
  881. }
  882. for( auto it = m_bus_name_to_code_map.begin(); it != m_bus_name_to_code_map.end(); )
  883. {
  884. if( codes_to_remove.contains( it->second ) )
  885. it = m_bus_name_to_code_map.erase( it );
  886. else
  887. ++it;
  888. }
  889. for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
  890. {
  891. sg->m_code = -1;
  892. sg->m_graph = nullptr;
  893. delete sg;
  894. }
  895. }
  896. void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
  897. const std::vector<SCH_ITEM*>& aItemList )
  898. {
  899. wxLogTrace( wxT( "Updating connectivity for sheet %s with %zu items" ),
  900. aSheet.Last()->GetFileName(), aItemList.size() );
  901. std::map<VECTOR2I, std::vector<SCH_ITEM*>> connection_map;
  902. auto updatePin = [&]( SCH_PIN* aPin, SCH_CONNECTION* aConn )
  903. {
  904. aConn->SetType( CONNECTION_TYPE::NET );
  905. // because calling the first time is not thread-safe
  906. wxString name = aPin->GetDefaultNetName( aSheet );
  907. aPin->ClearConnectedItems( aSheet );
  908. // power symbol pins need to be post-processed later
  909. if( aPin->IsGlobalPower() )
  910. {
  911. aConn->SetName( name );
  912. m_global_power_pins.emplace_back( std::make_pair( aSheet, aPin ) );
  913. }
  914. };
  915. for( SCH_ITEM* item : aItemList )
  916. {
  917. std::vector<VECTOR2I> points = item->GetConnectionPoints();
  918. item->ClearConnectedItems( aSheet );
  919. if( item->Type() == SCH_SHEET_T )
  920. {
  921. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  922. {
  923. pin->InitializeConnection( aSheet, this );
  924. pin->ClearConnectedItems( aSheet );
  925. connection_map[ pin->GetTextPos() ].push_back( pin );
  926. m_items.emplace_back( pin );
  927. }
  928. }
  929. else if( item->Type() == SCH_SYMBOL_T )
  930. {
  931. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  932. for( SCH_PIN* pin : symbol->GetPins( &aSheet ) )
  933. {
  934. m_items.emplace_back( pin );
  935. SCH_CONNECTION* conn = pin->InitializeConnection( aSheet, this );
  936. updatePin( pin, conn );
  937. connection_map[ pin->GetPosition() ].push_back( pin );
  938. }
  939. }
  940. else
  941. {
  942. m_items.emplace_back( item );
  943. SCH_CONNECTION* conn = item->InitializeConnection( aSheet, this );
  944. // Set bus/net property here so that the propagation code uses it
  945. switch( item->Type() )
  946. {
  947. case SCH_LINE_T:
  948. conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
  949. CONNECTION_TYPE::NET );
  950. break;
  951. case SCH_BUS_BUS_ENTRY_T:
  952. conn->SetType( CONNECTION_TYPE::BUS );
  953. // clean previous (old) links:
  954. static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
  955. static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
  956. break;
  957. case SCH_PIN_T:
  958. if( points.empty() )
  959. points = { static_cast<SCH_PIN*>( item )->GetPosition() };
  960. updatePin( static_cast<SCH_PIN*>( item ), conn );
  961. break;
  962. case SCH_BUS_WIRE_ENTRY_T:
  963. conn->SetType( CONNECTION_TYPE::NET );
  964. // clean previous (old) link:
  965. static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
  966. break;
  967. default:
  968. break;
  969. }
  970. for( const VECTOR2I& point : points )
  971. connection_map[ point ].push_back( item );
  972. }
  973. }
  974. for( const auto& it : connection_map )
  975. {
  976. std::vector<SCH_ITEM*> connection_vec = it.second;
  977. std::sort( connection_vec.begin(), connection_vec.end() );
  978. alg::remove_duplicates( connection_vec );
  979. // Pre-scan to see if we have a bus at this location
  980. SCH_LINE* busLine = aSheet.LastScreen()->GetBus( it.first );
  981. std::mutex update_mutex;
  982. auto update_lambda = [&]( SCH_ITEM* connected_item ) -> size_t
  983. {
  984. // Bus entries are special: they can have connection points in the
  985. // middle of a wire segment, because the junction algo doesn't split
  986. // the segment in two where you place a bus entry. This means that
  987. // bus entries that don't land on the end of a line segment need to
  988. // have "virtual" connection points to the segments they graphically
  989. // touch.
  990. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  991. {
  992. // If this location only has the connection point of the bus
  993. // entry itself, this means that either the bus entry is not
  994. // connected to anything graphically, or that it is connected to
  995. // a segment at some point other than at one of the endpoints.
  996. if( connection_vec.size() == 1 )
  997. {
  998. if( busLine )
  999. {
  1000. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  1001. bus_entry->m_connected_bus_item = busLine;
  1002. }
  1003. }
  1004. }
  1005. // Bus-to-bus entries are treated just like bus wires
  1006. else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
  1007. {
  1008. if( connection_vec.size() < 2 )
  1009. {
  1010. if( busLine )
  1011. {
  1012. auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
  1013. if( it.first == bus_entry->GetPosition() )
  1014. bus_entry->m_connected_bus_items[0] = busLine;
  1015. else
  1016. bus_entry->m_connected_bus_items[1] = busLine;
  1017. std::lock_guard<std::mutex> lock( update_mutex );
  1018. bus_entry->AddConnectionTo( aSheet, busLine );
  1019. busLine->AddConnectionTo( aSheet, bus_entry );
  1020. }
  1021. }
  1022. }
  1023. // Change junctions to be on bus junction layer if they are touching a bus
  1024. else if( connected_item->Type() == SCH_JUNCTION_T )
  1025. {
  1026. connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
  1027. }
  1028. for( SCH_ITEM* test_item : connection_vec )
  1029. {
  1030. bool bus_connection_ok = true;
  1031. if( test_item == connected_item )
  1032. continue;
  1033. // Set up the link between the bus entry net and the bus
  1034. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  1035. {
  1036. if( test_item->GetLayer() == LAYER_BUS )
  1037. {
  1038. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  1039. bus_entry->m_connected_bus_item = test_item;
  1040. }
  1041. }
  1042. // Bus entries only connect to bus lines on the end that is touching a bus line.
  1043. // If the user has overlapped another net line with the endpoint of the bus entry
  1044. // where the entry connects to a bus, we don't want to short-circuit it.
  1045. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  1046. {
  1047. bus_connection_ok = !busLine || test_item->GetLayer() == LAYER_BUS;
  1048. }
  1049. else if( test_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  1050. {
  1051. bus_connection_ok = !busLine || connected_item->GetLayer() == LAYER_BUS;
  1052. }
  1053. if( connected_item->ConnectionPropagatesTo( test_item ) &&
  1054. test_item->ConnectionPropagatesTo( connected_item ) &&
  1055. bus_connection_ok )
  1056. {
  1057. connected_item->AddConnectionTo( aSheet, test_item );
  1058. }
  1059. }
  1060. // If we got this far and did not find a connected bus item for a bus entry,
  1061. // we should do a manual scan in case there is a bus item on this connection
  1062. // point but we didn't pick it up earlier because there is *also* a net item here.
  1063. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  1064. {
  1065. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  1066. if( !bus_entry->m_connected_bus_item )
  1067. {
  1068. SCH_SCREEN* screen = aSheet.LastScreen();
  1069. SCH_LINE* bus = screen->GetBus( it.first );
  1070. if( bus )
  1071. bus_entry->m_connected_bus_item = bus;
  1072. }
  1073. }
  1074. return 1;
  1075. };
  1076. thread_pool& tp = GetKiCadThreadPool();
  1077. tp.push_loop( connection_vec.size(),
  1078. [&]( const int a, const int b)
  1079. {
  1080. for( int ii = a; ii < b; ++ii )
  1081. update_lambda( connection_vec[ii] );
  1082. });
  1083. tp.wait_for_tasks();
  1084. }
  1085. }
  1086. void CONNECTION_GRAPH::buildItemSubGraphs()
  1087. {
  1088. // Recache all bus aliases for later use
  1089. wxCHECK_RET( m_schematic, wxS( "Connection graph cannot be built without schematic pointer" ) );
  1090. SCH_SCREENS screens( m_schematic->Root() );
  1091. for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
  1092. {
  1093. for( const std::shared_ptr<BUS_ALIAS>& alias : screen->GetBusAliases() )
  1094. m_bus_alias_cache[alias->GetName()] = alias;
  1095. }
  1096. // Build subgraphs from items (on a per-sheet basis)
  1097. for( SCH_ITEM* item : m_items )
  1098. {
  1099. for( const auto& it : item->m_connection_map )
  1100. {
  1101. const SCH_SHEET_PATH& sheet = it.first;
  1102. SCH_CONNECTION* connection = it.second;
  1103. if( connection->SubgraphCode() == 0 )
  1104. {
  1105. CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
  1106. subgraph->m_code = m_last_subgraph_code++;
  1107. subgraph->m_sheet = sheet;
  1108. subgraph->AddItem( item );
  1109. connection->SetSubgraphCode( subgraph->m_code );
  1110. m_item_to_subgraph_map[item] = subgraph;
  1111. std::list<SCH_ITEM*> memberlist;
  1112. auto get_items =
  1113. [&]( SCH_ITEM* aItem ) -> bool
  1114. {
  1115. SCH_CONNECTION* conn = aItem->GetOrInitConnection( sheet, this );
  1116. bool unique = !( aItem->GetFlags() & CANDIDATE );
  1117. if( conn && !conn->SubgraphCode() )
  1118. aItem->SetFlags( CANDIDATE );
  1119. return ( unique && conn && ( conn->SubgraphCode() == 0 ) );
  1120. };
  1121. std::copy_if( item->ConnectedItems( sheet ).begin(),
  1122. item->ConnectedItems( sheet ).end(),
  1123. std::back_inserter( memberlist ), get_items );
  1124. for( SCH_ITEM* connected_item : memberlist )
  1125. {
  1126. if( connected_item->Type() == SCH_NO_CONNECT_T )
  1127. subgraph->m_no_connect = connected_item;
  1128. SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
  1129. wxASSERT( connected_conn );
  1130. if( connected_conn->SubgraphCode() == 0 )
  1131. {
  1132. connected_conn->SetSubgraphCode( subgraph->m_code );
  1133. m_item_to_subgraph_map[connected_item] = subgraph;
  1134. subgraph->AddItem( connected_item );
  1135. const SCH_ITEM_VEC& citemset = connected_item->ConnectedItems( sheet );
  1136. for( SCH_ITEM* citem : citemset )
  1137. {
  1138. if( citem->HasFlag( CANDIDATE ) )
  1139. continue;
  1140. if( get_items( citem ) )
  1141. memberlist.push_back( citem );
  1142. }
  1143. }
  1144. }
  1145. for( SCH_ITEM* connected_item : memberlist )
  1146. connected_item->ClearFlags( CANDIDATE );
  1147. subgraph->m_dirty = true;
  1148. m_subgraphs.push_back( subgraph );
  1149. }
  1150. }
  1151. }
  1152. }
  1153. void CONNECTION_GRAPH::resolveAllDrivers()
  1154. {
  1155. // Resolve drivers for subgraphs and propagate connectivity info
  1156. std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
  1157. std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
  1158. [&] ( const CONNECTION_SUBGRAPH* candidate )
  1159. {
  1160. return candidate->m_dirty;
  1161. } );
  1162. wxLogTrace( ConnTrace, wxT( "Resolving drivers for %zu subgraphs" ), dirty_graphs.size() );
  1163. std::vector<std::future<size_t>> returns( dirty_graphs.size() );
  1164. auto update_lambda = []( CONNECTION_SUBGRAPH* subgraph ) -> size_t
  1165. {
  1166. if( !subgraph->m_dirty )
  1167. return 0;
  1168. // Special processing for some items
  1169. for( SCH_ITEM* item : subgraph->m_items )
  1170. {
  1171. switch( item->Type() )
  1172. {
  1173. case SCH_NO_CONNECT_T:
  1174. subgraph->m_no_connect = item;
  1175. break;
  1176. case SCH_BUS_WIRE_ENTRY_T:
  1177. subgraph->m_bus_entry = item;
  1178. break;
  1179. case SCH_PIN_T:
  1180. {
  1181. auto pin = static_cast<SCH_PIN*>( item );
  1182. if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
  1183. subgraph->m_no_connect = item;
  1184. break;
  1185. }
  1186. default:
  1187. break;
  1188. }
  1189. }
  1190. subgraph->ResolveDrivers( true );
  1191. subgraph->m_dirty = false;
  1192. return 1;
  1193. };
  1194. thread_pool& tp = GetKiCadThreadPool();
  1195. tp.push_loop( dirty_graphs.size(),
  1196. [&]( const int a, const int b)
  1197. {
  1198. for( int ii = a; ii < b; ++ii )
  1199. update_lambda( dirty_graphs[ii] );
  1200. });
  1201. tp.wait_for_tasks();
  1202. // Now discard any non-driven subgraphs from further consideration
  1203. std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
  1204. [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
  1205. {
  1206. return candidate->m_driver;
  1207. } );
  1208. }
  1209. void CONNECTION_GRAPH::collectAllDriverValues()
  1210. {
  1211. // Check for subgraphs with the same net name but only weak drivers.
  1212. // For example, two wires that are both connected to hierarchical
  1213. // sheet pins that happen to have the same name, but are not the same.
  1214. for( auto&& subgraph : m_driver_subgraphs )
  1215. {
  1216. wxString full_name = subgraph->m_driver_connection->Name();
  1217. wxString name = subgraph->m_driver_connection->Name( true );
  1218. m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
  1219. // For vector buses, we need to cache the prefix also, as two different instances of the
  1220. // weakly driven pin may have the same prefix but different vector start and end. We need
  1221. // to treat those as needing renaming also, because otherwise if they end up on a sheet with
  1222. // common usage, they will be incorrectly merged.
  1223. if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
  1224. {
  1225. wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
  1226. m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
  1227. }
  1228. subgraph->m_dirty = true;
  1229. if( subgraph->m_strong_driver )
  1230. {
  1231. SCH_ITEM* driver = subgraph->m_driver;
  1232. SCH_SHEET_PATH sheet = subgraph->m_sheet;
  1233. switch( driver->Type() )
  1234. {
  1235. case SCH_LABEL_T:
  1236. case SCH_HIER_LABEL_T:
  1237. {
  1238. m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
  1239. break;
  1240. }
  1241. case SCH_GLOBAL_LABEL_T:
  1242. {
  1243. m_global_label_cache[name].push_back( subgraph );
  1244. break;
  1245. }
  1246. case SCH_PIN_T:
  1247. {
  1248. SCH_PIN* pin = static_cast<SCH_PIN*>( driver );
  1249. wxASSERT( pin->IsGlobalPower() );
  1250. m_global_label_cache[name].push_back( subgraph );
  1251. break;
  1252. }
  1253. default:
  1254. {
  1255. UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MILLIMETRES );
  1256. wxLogTrace( ConnTrace, wxS( "Unexpected strong driver %s" ),
  1257. driver->GetItemDescription( &unitsProvider, true ) );
  1258. break;
  1259. }
  1260. }
  1261. }
  1262. }
  1263. }
  1264. void CONNECTION_GRAPH::generateBusAliasMembers()
  1265. {
  1266. std::vector<CONNECTION_SUBGRAPH*> new_subgraphs;
  1267. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1268. {
  1269. SCH_ITEM_VEC vec = subgraph->GetAllBusLabels();
  1270. for( SCH_ITEM* item : vec )
  1271. {
  1272. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
  1273. SCH_CONNECTION dummy( item, subgraph->m_sheet );
  1274. dummy.SetGraph( this );
  1275. dummy.ConfigureFromLabel( label->GetShownText( &subgraph->m_sheet, false ) );
  1276. wxLogTrace( ConnTrace, wxS( "new bus label (%s)" ),
  1277. label->GetShownText( &subgraph->m_sheet, false ) );
  1278. for( const auto& conn : dummy.Members() )
  1279. {
  1280. wxString name = conn->FullLocalName();
  1281. CONNECTION_SUBGRAPH* new_sg = new CONNECTION_SUBGRAPH( this );
  1282. // This connection cannot form a part of the item because the item is not, itself
  1283. // connected to this subgraph. It exists as part of a virtual item that may be
  1284. // connected to other items but is not in the schematic.
  1285. SCH_CONNECTION* new_conn = new SCH_CONNECTION( item, subgraph->m_sheet );
  1286. new_conn->SetGraph( this );
  1287. new_conn->SetName( name );
  1288. new_conn->SetType( CONNECTION_TYPE::NET );
  1289. subgraph->StoreImplicitConnection( new_conn );
  1290. int code = assignNewNetCode( *new_conn );
  1291. wxLogTrace( ConnTrace, wxS( "SG(%ld), Adding full local name (%s) with sg (%d) "
  1292. "on subsheet %s" ),
  1293. subgraph->m_code, name, code, subgraph->m_sheet.PathHumanReadable() );
  1294. new_sg->m_driver_connection = new_conn;
  1295. new_sg->m_code = m_last_subgraph_code++;
  1296. new_sg->m_sheet = subgraph->GetSheet();
  1297. new_sg->m_is_bus_member = true;
  1298. new_sg->m_strong_driver = true;
  1299. /// Need to figure out why these sgs are not getting connected to their bus parents
  1300. NET_NAME_CODE_CACHE_KEY key = { new_sg->GetNetName(), code };
  1301. m_net_code_to_subgraphs_map[ key ].push_back( new_sg );
  1302. m_net_name_to_subgraphs_map[ name ].push_back( new_sg );
  1303. m_subgraphs.push_back( new_sg );
  1304. new_subgraphs.push_back( new_sg );
  1305. }
  1306. }
  1307. }
  1308. std::copy( new_subgraphs.begin(), new_subgraphs.end(),
  1309. std::back_inserter( m_driver_subgraphs ) );
  1310. }
  1311. void CONNECTION_GRAPH::generateGlobalPowerPinSubGraphs()
  1312. {
  1313. // Generate subgraphs for global power pins. These will be merged with other subgraphs
  1314. // on the same sheet in the next loop.
  1315. // These are NOT limited to power symbols, we support legacy invisible + power-in pins
  1316. // on non-power symbols.
  1317. std::unordered_map<int, CONNECTION_SUBGRAPH*> global_power_pin_subgraphs;
  1318. for( const auto& it : m_global_power_pins )
  1319. {
  1320. SCH_SHEET_PATH sheet = it.first;
  1321. SCH_PIN* pin = it.second;
  1322. if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParentSymbol()->IsPower() )
  1323. {
  1324. // ERC will warn about this: user has wired up an invisible pin
  1325. continue;
  1326. }
  1327. SCH_CONNECTION* connection = pin->GetOrInitConnection( sheet, this );
  1328. // If this pin already has a subgraph, don't need to process
  1329. if( !connection || connection->SubgraphCode() > 0 )
  1330. continue;
  1331. // Proper modern power symbols get their net name from the value field
  1332. // in the symbol, but we support legacy non-power symbols with global
  1333. // power connections based on invisible, power-in, pin's names.
  1334. if( pin->GetLibPin()->GetParentSymbol()->IsPower() )
  1335. connection->SetName( pin->GetParentSymbol()->GetValue( true, &sheet, false ) );
  1336. else
  1337. connection->SetName( pin->GetShownName() );
  1338. int code = assignNewNetCode( *connection );
  1339. connection->SetNetCode( code );
  1340. CONNECTION_SUBGRAPH* subgraph;
  1341. auto jj = global_power_pin_subgraphs.find( code );
  1342. if( jj != global_power_pin_subgraphs.end() )
  1343. {
  1344. subgraph = jj->second;
  1345. subgraph->AddItem( pin );
  1346. }
  1347. else
  1348. {
  1349. subgraph = new CONNECTION_SUBGRAPH( this );
  1350. subgraph->m_code = m_last_subgraph_code++;
  1351. subgraph->m_sheet = sheet;
  1352. subgraph->AddItem( pin );
  1353. subgraph->ResolveDrivers();
  1354. NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(), code };
  1355. m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
  1356. m_subgraphs.push_back( subgraph );
  1357. m_driver_subgraphs.push_back( subgraph );
  1358. global_power_pin_subgraphs[code] = subgraph;
  1359. }
  1360. connection->SetSubgraphCode( subgraph->m_code );
  1361. }
  1362. }
  1363. void CONNECTION_GRAPH::processSubGraphs()
  1364. {
  1365. // Here we do all the local (sheet) processing of each subgraph, including assigning net
  1366. // codes, merging subgraphs together that use label connections, etc.
  1367. // Cache remaining valid subgraphs by sheet path
  1368. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1369. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
  1370. std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
  1371. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1372. {
  1373. if( subgraph->m_absorbed )
  1374. continue;
  1375. SCH_CONNECTION* connection = subgraph->m_driver_connection;
  1376. SCH_SHEET_PATH sheet = subgraph->m_sheet;
  1377. wxString name = connection->Name();
  1378. // Test subgraphs with weak drivers for net name conflicts and fix them
  1379. unsigned suffix = 1;
  1380. auto create_new_name =
  1381. [&suffix]( SCH_CONNECTION* aConn ) -> wxString
  1382. {
  1383. wxString newName;
  1384. wxString suffixStr = std::to_wstring( suffix );
  1385. // For group buses with a prefix, we can add the suffix to the prefix.
  1386. // If they don't have a prefix, we force the creation of a prefix so that
  1387. // two buses don't get inadvertently shorted together.
  1388. if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
  1389. {
  1390. wxString prefix = aConn->BusPrefix();
  1391. if( prefix.empty() )
  1392. prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
  1393. wxString oldName = aConn->Name().AfterFirst( '{' );
  1394. newName << prefix << wxT( "_" ) << suffixStr << wxT( "{" ) << oldName;
  1395. aConn->ConfigureFromLabel( newName );
  1396. }
  1397. else
  1398. {
  1399. newName << aConn->Name() << wxT( "_" ) << suffixStr;
  1400. aConn->SetSuffix( wxString( wxT( "_" ) ) << suffixStr );
  1401. }
  1402. suffix++;
  1403. return newName;
  1404. };
  1405. if( !subgraph->m_strong_driver )
  1406. {
  1407. std::vector<CONNECTION_SUBGRAPH*> vec_empty;
  1408. std::vector<CONNECTION_SUBGRAPH*>* vec = &vec_empty;
  1409. if( m_net_name_to_subgraphs_map.count( name ) )
  1410. vec = &m_net_name_to_subgraphs_map.at( name );
  1411. // If we are a unique bus vector, check if we aren't actually unique because of another
  1412. // subgraph with a similar bus vector
  1413. if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
  1414. {
  1415. wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
  1416. if( m_net_name_to_subgraphs_map.count( prefixOnly ) )
  1417. vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
  1418. }
  1419. if( vec->size() > 1 )
  1420. {
  1421. wxString new_name = create_new_name( connection );
  1422. while( m_net_name_to_subgraphs_map.contains( new_name ) )
  1423. new_name = create_new_name( connection );
  1424. wxLogTrace( ConnTrace,
  1425. wxS( "%ld (%s) is weakly driven and not unique. Changing to %s." ),
  1426. subgraph->m_code, name, new_name );
  1427. alg::delete_matching( *vec, subgraph );
  1428. m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
  1429. name = new_name;
  1430. }
  1431. else if( subgraph->m_driver )
  1432. {
  1433. // If there is no conflict, promote sheet pins to be strong drivers so that they
  1434. // will be considered below for propagation/merging.
  1435. // It is possible for this to generate a conflict if the sheet pin has the same
  1436. // name as a global label on the same sheet, because global merging will then treat
  1437. // this subgraph as if it had a matching local label. So, for those cases, we
  1438. // don't apply this promotion
  1439. if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
  1440. {
  1441. bool conflict = false;
  1442. wxString global_name = connection->Name( true );
  1443. auto kk = m_net_name_to_subgraphs_map.find( global_name );
  1444. if( kk != m_net_name_to_subgraphs_map.end() )
  1445. {
  1446. // A global will conflict if it is on the same sheet as this subgraph, since
  1447. // it would be connected by implicit local label linking
  1448. std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
  1449. for( const CONNECTION_SUBGRAPH* candidate : candidates )
  1450. {
  1451. if( candidate->m_sheet == sheet )
  1452. conflict = true;
  1453. }
  1454. }
  1455. if( conflict )
  1456. {
  1457. wxLogTrace( ConnTrace,
  1458. wxS( "%ld (%s) skipped for promotion due to potential "
  1459. "conflict" ),
  1460. subgraph->m_code, name );
  1461. }
  1462. else
  1463. {
  1464. UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MILLIMETRES );
  1465. wxLogTrace( ConnTrace,
  1466. wxS( "%ld (%s) weakly driven by unique sheet pin %s, "
  1467. "promoting" ),
  1468. subgraph->m_code, name,
  1469. subgraph->m_driver->GetItemDescription( &unitsProvider, true ) );
  1470. subgraph->m_strong_driver = true;
  1471. }
  1472. }
  1473. }
  1474. }
  1475. // Assign net codes
  1476. if( connection->IsBus() )
  1477. {
  1478. int code = -1;
  1479. auto it = m_bus_name_to_code_map.find( name );
  1480. if( it != m_bus_name_to_code_map.end() )
  1481. {
  1482. code = it->second;
  1483. }
  1484. else
  1485. {
  1486. code = m_last_bus_code++;
  1487. m_bus_name_to_code_map[ name ] = code;
  1488. }
  1489. connection->SetBusCode( code );
  1490. assignNetCodesToBus( connection );
  1491. }
  1492. else
  1493. {
  1494. assignNewNetCode( *connection );
  1495. }
  1496. // Reset the flag for the next loop below
  1497. subgraph->m_dirty = true;
  1498. // Next, we merge together subgraphs that have label connections, and create
  1499. // neighbor links for subgraphs that are part of a bus on the same sheet.
  1500. // For merging, we consider each possible strong driver.
  1501. // If this subgraph doesn't have a strong driver, let's skip it, since there is no
  1502. // way it will be merged with anything.
  1503. if( !subgraph->m_strong_driver )
  1504. continue;
  1505. // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
  1506. // as the subgraph we are considering that has a strong driver.
  1507. // Weakly driven subgraphs are not considered since they will never be absorbed or
  1508. // form neighbor links.
  1509. std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
  1510. std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
  1511. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
  1512. std::back_inserter( candidate_subgraphs ),
  1513. [&] ( const CONNECTION_SUBGRAPH* candidate )
  1514. {
  1515. return ( !candidate->m_absorbed &&
  1516. candidate->m_strong_driver &&
  1517. candidate != subgraph );
  1518. } );
  1519. // This is a list of connections on the current subgraph to compare to the
  1520. // drivers of each candidate subgraph. If the current subgraph is a bus,
  1521. // we should consider each bus member.
  1522. std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
  1523. // Also check the main driving connection
  1524. connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
  1525. auto add_connections_to_check =
  1526. [&] ( CONNECTION_SUBGRAPH* aSubgraph )
  1527. {
  1528. for( SCH_ITEM* possible_driver : aSubgraph->m_items )
  1529. {
  1530. if( possible_driver == aSubgraph->m_driver )
  1531. continue;
  1532. auto c = getDefaultConnection( possible_driver, aSubgraph );
  1533. if( c )
  1534. {
  1535. if( c->Type() != aSubgraph->m_driver_connection->Type() )
  1536. continue;
  1537. if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
  1538. continue;
  1539. connections_to_check.push_back( c );
  1540. wxLogTrace( ConnTrace,
  1541. wxS( "%lu (%s): Adding secondary driver %s" ),
  1542. aSubgraph->m_code,
  1543. aSubgraph->m_driver_connection->Name( true ),
  1544. c->Name( true ) );
  1545. }
  1546. }
  1547. };
  1548. // Now add other strong drivers
  1549. // The actual connection attached to these items will have been overwritten
  1550. // by the chosen driver of the subgraph, so we need to create a dummy connection
  1551. add_connections_to_check( subgraph );
  1552. std::set<SCH_CONNECTION*> checked_connections;
  1553. for( unsigned i = 0; i < connections_to_check.size(); i++ )
  1554. {
  1555. auto member = connections_to_check[i];
  1556. // Don't check the same connection twice
  1557. if( !checked_connections.insert( member.get() ).second )
  1558. continue;
  1559. if( member->IsBus() )
  1560. {
  1561. connections_to_check.insert( connections_to_check.end(),
  1562. member->Members().begin(),
  1563. member->Members().end() );
  1564. }
  1565. wxString test_name = member->Name( true );
  1566. for( CONNECTION_SUBGRAPH* candidate : candidate_subgraphs )
  1567. {
  1568. if( candidate->m_absorbed || candidate == subgraph )
  1569. continue;
  1570. bool match = false;
  1571. if( candidate->m_driver_connection->Name( true ) == test_name )
  1572. {
  1573. match = true;
  1574. }
  1575. else
  1576. {
  1577. if( !candidate->m_multiple_drivers )
  1578. continue;
  1579. for( SCH_ITEM *driver : candidate->m_drivers )
  1580. {
  1581. if( driver == candidate->m_driver )
  1582. continue;
  1583. // Sheet pins are not candidates for merging
  1584. if( driver->Type() == SCH_SHEET_PIN_T )
  1585. continue;
  1586. if( driver->Type() == SCH_PIN_T )
  1587. {
  1588. auto pin = static_cast<SCH_PIN*>( driver );
  1589. if( pin->IsGlobalPower()
  1590. && pin->GetDefaultNetName( sheet ) == test_name )
  1591. {
  1592. match = true;
  1593. break;
  1594. }
  1595. }
  1596. else
  1597. {
  1598. wxASSERT( driver->Type() == SCH_LABEL_T ||
  1599. driver->Type() == SCH_GLOBAL_LABEL_T ||
  1600. driver->Type() == SCH_HIER_LABEL_T );
  1601. if( subgraph->GetNameForDriver( driver ) == test_name )
  1602. {
  1603. match = true;
  1604. break;
  1605. }
  1606. }
  1607. }
  1608. }
  1609. if( match )
  1610. {
  1611. if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
  1612. {
  1613. wxLogTrace( ConnTrace, wxS( "%lu (%s) has bus child %lu (%s)" ),
  1614. subgraph->m_code, connection->Name(),
  1615. candidate->m_code, member->Name() );
  1616. subgraph->m_bus_neighbors[member].insert( candidate );
  1617. candidate->m_bus_parents[member].insert( subgraph );
  1618. }
  1619. else if( connection->Type() == candidate->m_driver_connection->Type() )
  1620. {
  1621. wxLogTrace( ConnTrace, wxS( "%lu (%s) absorbs neighbor %lu (%s)" ),
  1622. subgraph->m_code, connection->Name(),
  1623. candidate->m_code, candidate->m_driver_connection->Name() );
  1624. // Candidate may have other non-chosen drivers we need to follow
  1625. add_connections_to_check( candidate );
  1626. subgraph->Absorb( candidate );
  1627. invalidated_subgraphs.insert( subgraph );
  1628. }
  1629. }
  1630. }
  1631. }
  1632. }
  1633. // Update any subgraph that was invalidated above
  1634. for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
  1635. {
  1636. if( subgraph->m_absorbed )
  1637. continue;
  1638. if( !subgraph->ResolveDrivers() )
  1639. continue;
  1640. if( subgraph->m_driver_connection->IsBus() )
  1641. assignNetCodesToBus( subgraph->m_driver_connection );
  1642. else
  1643. assignNewNetCode( *subgraph->m_driver_connection );
  1644. wxLogTrace( ConnTrace, wxS( "Re-resolving drivers for %lu (%s)" ), subgraph->m_code,
  1645. subgraph->m_driver_connection->Name() );
  1646. }
  1647. }
  1648. // TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
  1649. // to the same subgraph necessarily if it runs over and over again on the same
  1650. // sheet. We need:
  1651. //
  1652. // a) a cache of net/bus codes, like used before
  1653. // b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
  1654. // c) some way of trying to avoid changing net names. so we should keep track
  1655. // of the previous driver of a net, and if it comes down to choosing between
  1656. // equally-prioritized drivers, choose the one that already exists as a driver
  1657. // on some portion of the items.
  1658. void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* aChangedItemHandler, bool aUnconditional )
  1659. {
  1660. // Recache all bus aliases for later use
  1661. wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) );
  1662. SCH_SCREENS screens( m_schematic->Root() );
  1663. for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
  1664. {
  1665. for( const std::shared_ptr<BUS_ALIAS>& alias : screen->GetBusAliases() )
  1666. m_bus_alias_cache[alias->GetName()] = alias;
  1667. }
  1668. PROF_TIMER sub_graph( "buildItemSubGraphs" );
  1669. buildItemSubGraphs();
  1670. if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
  1671. sub_graph.Show();
  1672. /**
  1673. * TODO(JE): Net codes are non-deterministic. Fortunately, they are also not really used for
  1674. * anything. We should consider removing them entirely and just using net names everywhere.
  1675. */
  1676. resolveAllDrivers();
  1677. collectAllDriverValues();
  1678. generateGlobalPowerPinSubGraphs();
  1679. generateBusAliasMembers();
  1680. PROF_TIMER proc_sub_graph( "ProcessSubGraphs" );
  1681. processSubGraphs();
  1682. if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
  1683. proc_sub_graph.Show();
  1684. // Absorbed subgraphs should no longer be considered
  1685. alg::delete_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool
  1686. {
  1687. return candidate->m_absorbed;
  1688. } );
  1689. // Store global subgraphs for later reference
  1690. std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
  1691. std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
  1692. std::back_inserter( global_subgraphs ),
  1693. [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
  1694. {
  1695. return !candidate->m_local_driver;
  1696. } );
  1697. // Recache remaining valid subgraphs by sheet path
  1698. m_sheet_to_subgraphs_map.clear();
  1699. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1700. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
  1701. thread_pool& tp = GetKiCadThreadPool();
  1702. tp.push_loop( m_driver_subgraphs.size(),
  1703. [&]( const int a, const int b)
  1704. {
  1705. for( int ii = a; ii < b; ++ii )
  1706. m_driver_subgraphs[ii]->UpdateItemConnections();
  1707. });
  1708. tp.wait_for_tasks();
  1709. // Next time through the subgraphs, we do some post-processing to handle things like
  1710. // connecting bus members to their neighboring subgraphs, and then propagate connections
  1711. // through the hierarchy
  1712. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1713. {
  1714. if( !subgraph->m_dirty )
  1715. continue;
  1716. wxLogTrace( ConnTrace, wxS( "Processing %lu (%s) for propagation" ), subgraph->m_code,
  1717. subgraph->m_driver_connection->Name() );
  1718. // For subgraphs that are driven by a global (power port or label) and have more
  1719. // than one global driver, we need to seek out other subgraphs driven by the
  1720. // same name as the non-chosen driver and update them to match the chosen one.
  1721. if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
  1722. {
  1723. for( SCH_ITEM* driver : subgraph->m_drivers )
  1724. {
  1725. if( driver == subgraph->m_driver )
  1726. continue;
  1727. const wxString& secondary_name = subgraph->GetNameForDriver( driver );
  1728. if( secondary_name == subgraph->m_driver_connection->Name() )
  1729. continue;
  1730. bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
  1731. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN;
  1732. for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
  1733. {
  1734. if( candidate == subgraph )
  1735. continue;
  1736. if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
  1737. continue;
  1738. SCH_CONNECTION* conn = candidate->m_driver_connection;
  1739. if( conn->Name() == secondary_name )
  1740. {
  1741. wxLogTrace( ConnTrace, wxS( "Global %lu (%s) promoted to %s" ),
  1742. candidate->m_code, conn->Name(),
  1743. subgraph->m_driver_connection->Name() );
  1744. conn->Clone( *subgraph->m_driver_connection );
  1745. candidate->m_dirty = false;
  1746. propagateToNeighbors( candidate, false );
  1747. }
  1748. }
  1749. }
  1750. }
  1751. // This call will handle descending the hierarchy and updating child subgraphs
  1752. propagateToNeighbors( subgraph, false );
  1753. }
  1754. // After processing and allowing some to be skipped if they have hierarchical
  1755. // pins connecting both up and down the hierarchy, we check to see if any of them
  1756. // have not been processed. This would indicate that they do not have off-sheet connections
  1757. // but we still need to handle the subgraph
  1758. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1759. {
  1760. if( subgraph->m_dirty )
  1761. propagateToNeighbors( subgraph, true );
  1762. }
  1763. // Handle buses that have been linked together somewhere by member (net) connections.
  1764. // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
  1765. // For net subgraphs that have more than one bus parent, we need to ensure that those
  1766. // buses are linked together in the final netlist. The final name of each bus might not
  1767. // match the local name that was used to establish the parent-child relationship, because
  1768. // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
  1769. // we need to identify the appropriate bus members to link together (and their final names),
  1770. // and then update all instances of the old name in the hierarchy.
  1771. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1772. {
  1773. // All SGs should have been processed by propagateToNeighbors above
  1774. wxASSERT_MSG( !subgraph->m_dirty,
  1775. wxS( "Subgraph not processed by propagateToNeighbors!" ) );
  1776. if( subgraph->m_bus_parents.size() < 2 )
  1777. continue;
  1778. SCH_CONNECTION* conn = subgraph->m_driver_connection;
  1779. wxLogTrace( ConnTrace, wxS( "%lu (%s) has multiple bus parents" ),
  1780. subgraph->m_code, conn->Name() );
  1781. wxASSERT( conn->IsNet() );
  1782. for( const auto& ii : subgraph->m_bus_parents )
  1783. {
  1784. SCH_CONNECTION* link_member = ii.first.get();
  1785. for( CONNECTION_SUBGRAPH* parent : ii.second )
  1786. {
  1787. while( parent->m_absorbed )
  1788. parent = parent->m_absorbed_by;
  1789. SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
  1790. if( !match )
  1791. {
  1792. wxLogTrace( ConnTrace, wxS( "Warning: could not match %s inside %lu (%s)" ),
  1793. conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
  1794. continue;
  1795. }
  1796. if( conn->Name() != match->Name() )
  1797. {
  1798. wxString old_name = match->Name();
  1799. wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ),
  1800. parent->m_code, parent->m_driver_connection->Name(),
  1801. old_name, conn->Name() );
  1802. match->Clone( *conn );
  1803. auto jj = m_net_name_to_subgraphs_map.find( old_name );
  1804. if( jj == m_net_name_to_subgraphs_map.end() )
  1805. continue;
  1806. for( CONNECTION_SUBGRAPH* old_sg : jj->second )
  1807. {
  1808. while( old_sg->m_absorbed )
  1809. old_sg = old_sg->m_absorbed_by;
  1810. old_sg->m_driver_connection->Clone( *conn );
  1811. }
  1812. }
  1813. }
  1814. }
  1815. }
  1816. auto updateItemConnectionsTask =
  1817. [&]( CONNECTION_SUBGRAPH* subgraph ) -> size_t
  1818. {
  1819. // Make sure weakly-driven single-pin nets get the unconnected_ prefix
  1820. if( !subgraph->m_strong_driver && subgraph->m_drivers.size() == 1 &&
  1821. subgraph->m_driver->Type() == SCH_PIN_T )
  1822. {
  1823. SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
  1824. wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
  1825. subgraph->m_driver_connection->ConfigureFromLabel( name );
  1826. }
  1827. subgraph->m_dirty = false;
  1828. subgraph->UpdateItemConnections();
  1829. // No other processing to do on buses
  1830. if( subgraph->m_driver_connection->IsBus() )
  1831. return 0;
  1832. // As a visual aid, we can check sheet pins that are driven by themselves to see
  1833. // if they should be promoted to buses
  1834. if( subgraph->m_driver && subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
  1835. {
  1836. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
  1837. if( SCH_SHEET* sheet = pin->GetParent() )
  1838. {
  1839. wxString pinText = pin->GetShownText( false );
  1840. SCH_SCREEN* screen = sheet->GetScreen();
  1841. for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
  1842. {
  1843. SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
  1844. if( label->GetShownText( &subgraph->m_sheet, false ) == pinText )
  1845. {
  1846. SCH_SHEET_PATH path = subgraph->m_sheet;
  1847. path.push_back( sheet );
  1848. SCH_CONNECTION* parent_conn = label->Connection( &path );
  1849. if( parent_conn && parent_conn->IsBus() )
  1850. subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
  1851. break;
  1852. }
  1853. }
  1854. if( subgraph->m_driver_connection->IsBus() )
  1855. return 0;
  1856. }
  1857. }
  1858. return 1;
  1859. };
  1860. tp.push_loop( m_driver_subgraphs.size(),
  1861. [&]( const int a, const int b)
  1862. {
  1863. for( int ii = a; ii < b; ++ii )
  1864. updateItemConnectionsTask( m_driver_subgraphs[ii] );
  1865. });
  1866. tp.wait_for_tasks();
  1867. m_net_code_to_subgraphs_map.clear();
  1868. m_net_name_to_subgraphs_map.clear();
  1869. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1870. {
  1871. NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(),
  1872. subgraph->m_driver_connection->NetCode() };
  1873. m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
  1874. m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
  1875. }
  1876. std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Prj().GetProjectFile().m_NetSettings;
  1877. std::map<wxString, wxString> oldAssignments = netSettings->m_NetClassLabelAssignments;
  1878. std::set<wxString> affectedNetclassNetAssignments;
  1879. netSettings->m_NetClassLabelAssignments.clear();
  1880. // Used to track where netclasses are assigned to a net from a bus netclass
  1881. // definition. Without this tracking, depending on the order of testing for
  1882. // netclasses, an already-assigned netclass on a bus member can be deleted
  1883. // if an explicitly labelled member net does not have a netclass assigned.
  1884. std::unordered_set<wxString> netclassAssignedFromBus;
  1885. auto dirtySubgraphs =
  1886. [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
  1887. {
  1888. if( aChangedItemHandler )
  1889. {
  1890. for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
  1891. {
  1892. for( SCH_ITEM* item : subgraph->m_items )
  1893. (*aChangedItemHandler)( item );
  1894. }
  1895. }
  1896. };
  1897. auto checkNetclassDrivers =
  1898. [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
  1899. {
  1900. const CONNECTION_SUBGRAPH* driverSubgraph = nullptr;
  1901. wxString netclass;
  1902. wxCHECK_RET( !subgraphs.empty(), wxS( "Invalid empty subgraph" ) );
  1903. for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
  1904. {
  1905. for( SCH_ITEM* item : subgraph->m_items )
  1906. {
  1907. const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProviders =
  1908. subgraph->GetNetclassesForDriver( item, false );
  1909. if( netclassesWithProviders.size() > 0 )
  1910. {
  1911. netclass = netclassesWithProviders[0].first;
  1912. break;
  1913. }
  1914. }
  1915. if( !netclass.IsEmpty() )
  1916. {
  1917. driverSubgraph = subgraph;
  1918. break;
  1919. }
  1920. }
  1921. if( !driverSubgraph )
  1922. driverSubgraph = subgraphs.front();
  1923. const wxString netname = driverSubgraph->GetNetName();
  1924. // Handle netclass assignments on buses
  1925. if( driverSubgraph->m_driver_connection->IsBus() )
  1926. {
  1927. auto processBusMember = [&, this]( const SCH_CONNECTION* member )
  1928. {
  1929. if( !netclass.IsEmpty() )
  1930. {
  1931. netSettings->m_NetClassLabelAssignments[member->Name()] = netclass;
  1932. netclassAssignedFromBus.insert( member->Name() );
  1933. }
  1934. auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
  1935. if( oldAssignments.count( member->Name() ) )
  1936. {
  1937. if( oldAssignments[member->Name()] != netclass )
  1938. {
  1939. affectedNetclassNetAssignments.insert( member->Name() );
  1940. if( ii != m_net_name_to_subgraphs_map.end() )
  1941. dirtySubgraphs( ii->second );
  1942. }
  1943. }
  1944. else if( !netclass.IsEmpty() )
  1945. {
  1946. affectedNetclassNetAssignments.insert( member->Name() );
  1947. if( ii != m_net_name_to_subgraphs_map.end() )
  1948. dirtySubgraphs( ii->second );
  1949. }
  1950. };
  1951. for( const std::shared_ptr<SCH_CONNECTION>& member :
  1952. driverSubgraph->m_driver_connection->Members() )
  1953. {
  1954. // Check if this member itself is a bus (which can be the case
  1955. // for vector buses as members of a bus, see
  1956. // https://gitlab.com/kicad/code/kicad/-/issues/16545
  1957. if( member->IsBus() )
  1958. {
  1959. for( const std::shared_ptr<SCH_CONNECTION>& nestedMember :
  1960. member->Members() )
  1961. {
  1962. processBusMember( nestedMember.get() );
  1963. }
  1964. }
  1965. else
  1966. {
  1967. processBusMember( member.get() );
  1968. }
  1969. }
  1970. }
  1971. // Handle netclass assignments on nets / overarching bus definitions.
  1972. // Note: currently a netclass definition on a bus will override any
  1973. // on a net. This will be resolved with multiple netclasses.
  1974. if( !netclassAssignedFromBus.count( netname ) )
  1975. {
  1976. if( !netclass.IsEmpty() )
  1977. {
  1978. netSettings->m_NetClassLabelAssignments[netname] = netclass;
  1979. }
  1980. if( oldAssignments.count( netname ) )
  1981. {
  1982. if( oldAssignments[netname] != netclass )
  1983. {
  1984. affectedNetclassNetAssignments.insert( netname );
  1985. dirtySubgraphs( subgraphs );
  1986. }
  1987. }
  1988. else if( !netclass.IsEmpty() )
  1989. {
  1990. affectedNetclassNetAssignments.insert( netname );
  1991. dirtySubgraphs( subgraphs );
  1992. }
  1993. }
  1994. };
  1995. for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
  1996. checkNetclassDrivers( subgraphs );
  1997. if( !aUnconditional )
  1998. {
  1999. for( auto& [ netname, netclass ] : oldAssignments )
  2000. {
  2001. if( netSettings->m_NetClassLabelAssignments.count( netname )
  2002. || affectedNetclassNetAssignments.count( netname ) )
  2003. {
  2004. continue;
  2005. }
  2006. netSettings->m_NetClassLabelAssignments[ netname ] = netclass;
  2007. }
  2008. }
  2009. }
  2010. int CONNECTION_GRAPH::getOrCreateNetCode( const wxString& aNetName )
  2011. {
  2012. int code;
  2013. auto it = m_net_name_to_code_map.find( aNetName );
  2014. if( it == m_net_name_to_code_map.end() )
  2015. {
  2016. code = m_last_net_code++;
  2017. m_net_name_to_code_map[ aNetName ] = code;
  2018. }
  2019. else
  2020. {
  2021. code = it->second;
  2022. }
  2023. return code;
  2024. }
  2025. int CONNECTION_GRAPH::assignNewNetCode( SCH_CONNECTION& aConnection )
  2026. {
  2027. int code = getOrCreateNetCode( aConnection.Name() );
  2028. aConnection.SetNetCode( code );
  2029. return code;
  2030. }
  2031. void CONNECTION_GRAPH::assignNetCodesToBus( SCH_CONNECTION* aConnection )
  2032. {
  2033. std::vector<std::shared_ptr<SCH_CONNECTION>> connections_to_check( aConnection->Members() );
  2034. for( unsigned i = 0; i < connections_to_check.size(); i++ )
  2035. {
  2036. const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
  2037. if( member->IsBus() )
  2038. {
  2039. connections_to_check.insert( connections_to_check.end(),
  2040. member->Members().begin(),
  2041. member->Members().end() );
  2042. continue;
  2043. }
  2044. assignNewNetCode( *member );
  2045. }
  2046. }
  2047. void CONNECTION_GRAPH::propagateToNeighbors( CONNECTION_SUBGRAPH* aSubgraph, bool aForce )
  2048. {
  2049. SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
  2050. std::vector<CONNECTION_SUBGRAPH*> search_list;
  2051. std::unordered_set<CONNECTION_SUBGRAPH*> visited;
  2052. std::unordered_set<SCH_CONNECTION*> stale_bus_members;
  2053. auto visit =[&]( CONNECTION_SUBGRAPH* aParent )
  2054. {
  2055. for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
  2056. {
  2057. SCH_SHEET_PATH path = aParent->m_sheet;
  2058. path.push_back( pin->GetParent() );
  2059. auto it = m_sheet_to_subgraphs_map.find( path );
  2060. if( it == m_sheet_to_subgraphs_map.end() )
  2061. continue;
  2062. for( CONNECTION_SUBGRAPH* candidate : it->second )
  2063. {
  2064. if( !candidate->m_strong_driver
  2065. || candidate->m_hier_ports.empty()
  2066. || visited.contains( candidate ) )
  2067. {
  2068. continue;
  2069. }
  2070. for( SCH_HIERLABEL* label : candidate->m_hier_ports )
  2071. {
  2072. if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
  2073. {
  2074. wxLogTrace( ConnTrace, wxS( "%lu: found child %lu (%s)" ), aParent->m_code,
  2075. candidate->m_code, candidate->m_driver_connection->Name() );
  2076. candidate->m_hier_parent = aParent;
  2077. aParent->m_hier_children.insert( candidate );
  2078. wxASSERT( candidate->m_graph == aParent->m_graph );
  2079. search_list.push_back( candidate );
  2080. break;
  2081. }
  2082. }
  2083. }
  2084. }
  2085. for( SCH_HIERLABEL* label : aParent->m_hier_ports )
  2086. {
  2087. SCH_SHEET_PATH path = aParent->m_sheet;
  2088. path.pop_back();
  2089. auto it = m_sheet_to_subgraphs_map.find( path );
  2090. if( it == m_sheet_to_subgraphs_map.end() )
  2091. continue;
  2092. for( CONNECTION_SUBGRAPH* candidate : it->second )
  2093. {
  2094. if( candidate->m_hier_pins.empty()
  2095. || visited.contains( candidate )
  2096. || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
  2097. {
  2098. continue;
  2099. }
  2100. const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
  2101. for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
  2102. {
  2103. // If the last sheet UUIDs won't match, no need to check the full path
  2104. if( pin->GetParent()->m_Uuid != last_parent_uuid )
  2105. continue;
  2106. SCH_SHEET_PATH pin_path = path;
  2107. pin_path.push_back( pin->GetParent() );
  2108. if( pin_path != aParent->m_sheet )
  2109. continue;
  2110. if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
  2111. {
  2112. wxLogTrace( ConnTrace, wxS( "%lu: found additional parent %lu (%s)" ),
  2113. aParent->m_code, candidate->m_code,
  2114. candidate->m_driver_connection->Name() );
  2115. aParent->m_hier_children.insert( candidate );
  2116. search_list.push_back( candidate );
  2117. break;
  2118. }
  2119. }
  2120. }
  2121. }
  2122. };
  2123. auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph )
  2124. {
  2125. for( const auto& kv : aParentGraph->m_bus_neighbors )
  2126. {
  2127. for( CONNECTION_SUBGRAPH* neighbor : kv.second )
  2128. {
  2129. // May have been absorbed but won't have been deleted
  2130. while( neighbor->m_absorbed )
  2131. neighbor = neighbor->m_absorbed_by;
  2132. SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
  2133. // Now member may be out of date, since we just cloned the
  2134. // connection from higher up in the hierarchy. We need to
  2135. // figure out what the actual new connection is.
  2136. SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
  2137. if( !member )
  2138. {
  2139. // Try harder: we might match on a secondary driver
  2140. for( CONNECTION_SUBGRAPH* sg : kv.second )
  2141. {
  2142. if( sg->m_multiple_drivers )
  2143. {
  2144. SCH_SHEET_PATH sheet = sg->m_sheet;
  2145. for( SCH_ITEM* driver : sg->m_drivers )
  2146. {
  2147. auto c = getDefaultConnection( driver, sg );
  2148. member = matchBusMember( parent, c.get() );
  2149. if( member )
  2150. break;
  2151. }
  2152. }
  2153. if( member )
  2154. break;
  2155. }
  2156. }
  2157. // This is bad, probably an ERC error
  2158. if( !member )
  2159. {
  2160. wxLogTrace( ConnTrace, wxS( "Could not match bus member %s in %s" ),
  2161. kv.first->Name(), parent->Name() );
  2162. continue;
  2163. }
  2164. auto neighbor_conn = neighbor->m_driver_connection;
  2165. auto neighbor_name = neighbor_conn->Name();
  2166. // Matching name: no update needed
  2167. if( neighbor_name == member->Name() )
  2168. continue;
  2169. // Was this neighbor already updated from a different sheet? Don't rename it again
  2170. if( neighbor_conn->Sheet() != neighbor->m_sheet )
  2171. continue;
  2172. // Safety check against infinite recursion
  2173. wxASSERT( neighbor_conn->IsNet() );
  2174. wxLogTrace( ConnTrace, wxS( "%lu (%s) connected to bus member %s (local %s)" ),
  2175. neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
  2176. // Take whichever name is higher priority
  2177. if( CONNECTION_SUBGRAPH::GetDriverPriority( neighbor->m_driver )
  2178. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  2179. {
  2180. member->Clone( *neighbor_conn );
  2181. stale_bus_members.insert( member );
  2182. }
  2183. else
  2184. {
  2185. neighbor_conn->Clone( *member );
  2186. recacheSubgraphName( neighbor, neighbor_name );
  2187. // Recurse onto this neighbor in case it needs to re-propagate
  2188. neighbor->m_dirty = true;
  2189. propagateToNeighbors( neighbor, aForce );
  2190. }
  2191. }
  2192. }
  2193. };
  2194. // If we are a bus, we must propagate to local neighbors and then the hierarchy
  2195. if( conn->IsBus() )
  2196. propagate_bus_neighbors( aSubgraph );
  2197. // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
  2198. // If we only have one or the other, process (we can either go bottom-up or top-down depending
  2199. // on which subgraph comes up first)
  2200. if( !aForce && !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
  2201. {
  2202. wxLogTrace( ConnTrace, wxS( "%lu (%s) has both hier ports and pins; deferring processing" ),
  2203. aSubgraph->m_code, conn->Name() );
  2204. return;
  2205. }
  2206. else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
  2207. {
  2208. wxLogTrace( ConnTrace,
  2209. wxS( "%lu (%s) has no hier pins or ports on sheet %s; marking clean" ),
  2210. aSubgraph->m_code, conn->Name(), aSubgraph->m_sheet.PathHumanReadable() );
  2211. aSubgraph->m_dirty = false;
  2212. return;
  2213. }
  2214. visited.insert( aSubgraph );
  2215. wxLogTrace( ConnTrace, wxS( "Propagating %lu (%s) to subsheets" ),
  2216. aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
  2217. visit( aSubgraph );
  2218. for( unsigned i = 0; i < search_list.size(); i++ )
  2219. {
  2220. auto child = search_list[i];
  2221. if( visited.insert( child ).second )
  2222. visit( child );
  2223. child->m_dirty = false;
  2224. }
  2225. // Now, find the best driver for this chain of subgraphs
  2226. CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
  2227. CONNECTION_SUBGRAPH::PRIORITY highest =
  2228. CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver );
  2229. bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
  2230. wxString bestName = aSubgraph->m_driver_connection->Name();
  2231. // Check if a subsheet has a higher-priority connection to the same net
  2232. if( highest < CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  2233. {
  2234. for( CONNECTION_SUBGRAPH* subgraph : visited )
  2235. {
  2236. if( subgraph == aSubgraph )
  2237. continue;
  2238. CONNECTION_SUBGRAPH::PRIORITY priority =
  2239. CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
  2240. bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
  2241. wxString candidateName = subgraph->m_driver_connection->Name();
  2242. bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
  2243. bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
  2244. // Pick a better driving subgraph if it:
  2245. // a) has a power pin or global driver
  2246. // b) is a strong driver and we're a weak driver
  2247. // c) is a higher priority strong driver
  2248. // d) matches our priority, is a strong driver, and has a shorter path
  2249. // e) matches our strength and is at least as short, and is alphabetically lower
  2250. if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
  2251. ( !bestIsStrong && candidateStrong ) ||
  2252. ( priority > highest && candidateStrong ) ||
  2253. ( priority == highest && candidateStrong && shorterPath ) ||
  2254. ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
  2255. ( candidateName < bestName ) ) )
  2256. {
  2257. bestDriver = subgraph;
  2258. highest = priority;
  2259. bestIsStrong = candidateStrong;
  2260. bestName = candidateName;
  2261. }
  2262. }
  2263. }
  2264. if( bestDriver != aSubgraph )
  2265. {
  2266. wxLogTrace( ConnTrace, wxS( "%lu (%s) overridden by new driver %lu (%s)" ),
  2267. aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
  2268. bestDriver->m_driver_connection->Name() );
  2269. }
  2270. conn = bestDriver->m_driver_connection;
  2271. for( CONNECTION_SUBGRAPH* subgraph : visited )
  2272. {
  2273. wxString old_name = subgraph->m_driver_connection->Name();
  2274. subgraph->m_driver_connection->Clone( *conn );
  2275. if( old_name != conn->Name() )
  2276. recacheSubgraphName( subgraph, old_name );
  2277. if( conn->IsBus() )
  2278. propagate_bus_neighbors( subgraph );
  2279. }
  2280. // Somewhere along the way, a bus member may have been upgraded to a global or power label.
  2281. // Because this can happen anywhere, we need a second pass to update all instances of that bus
  2282. // member to have the correct connection info
  2283. if( conn->IsBus() && !stale_bus_members.empty() )
  2284. {
  2285. std::unordered_set<SCH_CONNECTION*> cached_members = stale_bus_members;
  2286. for( SCH_CONNECTION* stale_member : cached_members )
  2287. {
  2288. for( CONNECTION_SUBGRAPH* subgraph : visited )
  2289. {
  2290. SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
  2291. stale_member );
  2292. if( !member )
  2293. {
  2294. wxLogTrace( ConnTrace, wxS( "WARNING: failed to match stale member %s in %s." ),
  2295. stale_member->Name(), subgraph->m_driver_connection->Name() );
  2296. continue;
  2297. }
  2298. wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ), subgraph->m_code,
  2299. subgraph->m_driver_connection->Name(), member->LocalName(),
  2300. stale_member->Name() );
  2301. member->Clone( *stale_member );
  2302. propagate_bus_neighbors( subgraph );
  2303. }
  2304. }
  2305. }
  2306. aSubgraph->m_dirty = false;
  2307. }
  2308. std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
  2309. CONNECTION_SUBGRAPH* aSubgraph )
  2310. {
  2311. std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
  2312. switch( aItem->Type() )
  2313. {
  2314. case SCH_PIN_T:
  2315. {
  2316. SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
  2317. if( pin->IsGlobalPower() )
  2318. c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
  2319. break;
  2320. }
  2321. case SCH_GLOBAL_LABEL_T:
  2322. case SCH_HIER_LABEL_T:
  2323. case SCH_LABEL_T:
  2324. {
  2325. c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
  2326. break;
  2327. }
  2328. default:
  2329. break;
  2330. }
  2331. if( c )
  2332. {
  2333. c->SetGraph( this );
  2334. c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
  2335. }
  2336. return c;
  2337. }
  2338. SCH_CONNECTION* CONNECTION_GRAPH::matchBusMember( SCH_CONNECTION* aBusConnection,
  2339. SCH_CONNECTION* aSearch )
  2340. {
  2341. wxASSERT( aBusConnection->IsBus() );
  2342. SCH_CONNECTION* match = nullptr;
  2343. if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
  2344. {
  2345. // Vector bus: compare against index, because we allow the name
  2346. // to be different
  2347. for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
  2348. {
  2349. if( bus_member->VectorIndex() == aSearch->VectorIndex() )
  2350. {
  2351. match = bus_member.get();
  2352. break;
  2353. }
  2354. }
  2355. }
  2356. else
  2357. {
  2358. // Group bus
  2359. for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
  2360. {
  2361. // Vector inside group: compare names, because for bus groups
  2362. // we expect the naming to be consistent across all usages
  2363. // TODO(JE) explain this in the docs
  2364. if( c->Type() == CONNECTION_TYPE::BUS )
  2365. {
  2366. for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
  2367. {
  2368. if( bus_member->LocalName() == aSearch->LocalName() )
  2369. {
  2370. match = bus_member.get();
  2371. break;
  2372. }
  2373. }
  2374. }
  2375. else if( c->LocalName() == aSearch->LocalName() )
  2376. {
  2377. match = c.get();
  2378. break;
  2379. }
  2380. }
  2381. }
  2382. return match;
  2383. }
  2384. void CONNECTION_GRAPH::recacheSubgraphName( CONNECTION_SUBGRAPH* aSubgraph,
  2385. const wxString& aOldName )
  2386. {
  2387. auto it = m_net_name_to_subgraphs_map.find( aOldName );
  2388. if( it != m_net_name_to_subgraphs_map.end() )
  2389. {
  2390. std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
  2391. alg::delete_matching( vec, aSubgraph );
  2392. }
  2393. wxLogTrace( ConnTrace, wxS( "recacheSubgraphName: %s => %s" ), aOldName,
  2394. aSubgraph->m_driver_connection->Name() );
  2395. m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
  2396. }
  2397. std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
  2398. {
  2399. auto it = m_bus_alias_cache.find( aName );
  2400. return it != m_bus_alias_cache.end() ? it->second : nullptr;
  2401. }
  2402. std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
  2403. {
  2404. std::vector<const CONNECTION_SUBGRAPH*> ret;
  2405. for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
  2406. {
  2407. // Graph is supposed to be up-to-date before calling this
  2408. wxASSERT( !subgraph->m_dirty );
  2409. if( !subgraph->m_driver )
  2410. continue;
  2411. SCH_SHEET_PATH* sheet = &subgraph->m_sheet;
  2412. SCH_CONNECTION* connection = subgraph->m_driver->Connection( sheet );
  2413. if( !connection->IsBus() )
  2414. continue;
  2415. auto labels = subgraph->GetVectorBusLabels();
  2416. if( labels.size() > 1 )
  2417. {
  2418. bool different = false;
  2419. wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText( sheet, false );
  2420. for( unsigned i = 1; i < labels.size(); ++i )
  2421. {
  2422. if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText( sheet, false ) != first )
  2423. {
  2424. different = true;
  2425. break;
  2426. }
  2427. }
  2428. if( !different )
  2429. continue;
  2430. wxLogTrace( ConnTrace, wxS( "SG %ld (%s) has multiple bus labels" ), subgraph->m_code,
  2431. connection->Name() );
  2432. ret.push_back( subgraph );
  2433. }
  2434. }
  2435. return ret;
  2436. }
  2437. wxString CONNECTION_GRAPH::GetResolvedSubgraphName( const CONNECTION_SUBGRAPH* aSubGraph ) const
  2438. {
  2439. wxString retval = aSubGraph->GetNetName();
  2440. bool found = false;
  2441. // This is a hacky way to find the true subgraph net name (why do we not store it?)
  2442. // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
  2443. for( auto it = m_net_name_to_subgraphs_map.begin();
  2444. it != m_net_name_to_subgraphs_map.end() && !found; ++it )
  2445. {
  2446. for( CONNECTION_SUBGRAPH* graph : it->second )
  2447. {
  2448. if( graph == aSubGraph )
  2449. {
  2450. retval = it->first;
  2451. found = true;
  2452. break;
  2453. }
  2454. }
  2455. }
  2456. return retval;
  2457. }
  2458. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::FindSubgraphByName( const wxString& aNetName,
  2459. const SCH_SHEET_PATH& aPath )
  2460. {
  2461. auto it = m_net_name_to_subgraphs_map.find( aNetName );
  2462. if( it == m_net_name_to_subgraphs_map.end() )
  2463. return nullptr;
  2464. for( CONNECTION_SUBGRAPH* sg : it->second )
  2465. {
  2466. // Cache is supposed to be valid by now
  2467. wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
  2468. if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
  2469. return sg;
  2470. }
  2471. return nullptr;
  2472. }
  2473. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::FindFirstSubgraphByName( const wxString& aNetName )
  2474. {
  2475. auto it = m_net_name_to_subgraphs_map.find( aNetName );
  2476. if( it == m_net_name_to_subgraphs_map.end() )
  2477. return nullptr;
  2478. wxASSERT( !it->second.empty() );
  2479. return it->second[0];
  2480. }
  2481. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::GetSubgraphForItem( SCH_ITEM* aItem ) const
  2482. {
  2483. auto it = m_item_to_subgraph_map.find( aItem );
  2484. CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
  2485. while( ret && ret->m_absorbed )
  2486. ret = ret->m_absorbed_by;
  2487. return ret;
  2488. }
  2489. const std::vector<CONNECTION_SUBGRAPH*>
  2490. CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
  2491. {
  2492. std::vector<CONNECTION_SUBGRAPH*> subgraphs;
  2493. auto it = m_net_name_to_subgraphs_map.find( aNetName );
  2494. if( it == m_net_name_to_subgraphs_map.end() )
  2495. return subgraphs;
  2496. return it->second;
  2497. }
  2498. int CONNECTION_GRAPH::RunERC()
  2499. {
  2500. int error_count = 0;
  2501. wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::RunERC" ) );
  2502. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  2503. // We don't want to run many ERC checks more than once on a given screen even though it may
  2504. // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
  2505. std::set<SCH_ITEM*> seenDriverInstances;
  2506. for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
  2507. {
  2508. // There shouldn't be any null sub-graph pointers.
  2509. wxCHECK2( subgraph, continue );
  2510. // Graph is supposed to be up-to-date before calling RunERC()
  2511. wxASSERT( !subgraph->m_dirty );
  2512. if( subgraph->m_absorbed )
  2513. continue;
  2514. if( seenDriverInstances.count( subgraph->m_driver ) )
  2515. continue;
  2516. if( subgraph->m_driver )
  2517. seenDriverInstances.insert( subgraph->m_driver );
  2518. /**
  2519. * NOTE:
  2520. *
  2521. * We could check that labels attached to bus subgraphs follow the
  2522. * proper format (i.e. actually define a bus).
  2523. *
  2524. * This check doesn't need to be here right now because labels
  2525. * won't actually be connected to bus wires if they aren't in the right
  2526. * format due to their TestDanglingEnds() implementation.
  2527. */
  2528. if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
  2529. {
  2530. if( !ercCheckMultipleDrivers( subgraph ) )
  2531. error_count++;
  2532. }
  2533. subgraph->ResolveDrivers( false );
  2534. if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
  2535. {
  2536. if( !ercCheckBusToNetConflicts( subgraph ) )
  2537. error_count++;
  2538. }
  2539. if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
  2540. {
  2541. if( !ercCheckBusToBusEntryConflicts( subgraph ) )
  2542. error_count++;
  2543. }
  2544. if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
  2545. {
  2546. if( !ercCheckBusToBusConflicts( subgraph ) )
  2547. error_count++;
  2548. }
  2549. if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
  2550. {
  2551. if( !ercCheckFloatingWires( subgraph ) )
  2552. error_count++;
  2553. }
  2554. if( settings.IsTestEnabled( ERCE_UNCONNECTED_WIRE_ENDPOINT ) )
  2555. {
  2556. if( !ercCheckDanglingWireEndpoints( subgraph ) )
  2557. error_count++;
  2558. }
  2559. if( settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED )
  2560. || settings.IsTestEnabled( ERCE_NOCONNECT_NOT_CONNECTED )
  2561. || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  2562. {
  2563. if( !ercCheckNoConnects( subgraph ) )
  2564. error_count++;
  2565. }
  2566. if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
  2567. || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
  2568. {
  2569. if( !ercCheckLabels( subgraph ) )
  2570. error_count++;
  2571. }
  2572. }
  2573. // Hierarchical sheet checking is done at the schematic level
  2574. if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL )
  2575. || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  2576. {
  2577. error_count += ercCheckHierSheets();
  2578. }
  2579. if( settings.IsTestEnabled( ERCE_NETCLASS_CONFLICT ) )
  2580. {
  2581. for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
  2582. {
  2583. if( !ercCheckNetclassConflicts( subgraphs ) )
  2584. error_count++;
  2585. }
  2586. }
  2587. if( settings.IsTestEnabled( ERCE_SINGLE_GLOBAL_LABEL ) )
  2588. {
  2589. error_count += ercCheckSingleGlobalLabel();
  2590. }
  2591. return error_count;
  2592. }
  2593. bool CONNECTION_GRAPH::ercCheckMultipleDrivers( const CONNECTION_SUBGRAPH* aSubgraph )
  2594. {
  2595. wxCHECK( aSubgraph, false );
  2596. if( aSubgraph->m_multiple_drivers )
  2597. {
  2598. for( SCH_ITEM* driver : aSubgraph->m_drivers )
  2599. {
  2600. if( driver == aSubgraph->m_driver )
  2601. continue;
  2602. if( driver->Type() == SCH_GLOBAL_LABEL_T
  2603. || driver->Type() == SCH_HIER_LABEL_T
  2604. || driver->Type() == SCH_LABEL_T
  2605. || ( driver->Type() == SCH_PIN_T
  2606. && static_cast<SCH_PIN*>( driver )->IsGlobalPower() ) )
  2607. {
  2608. const wxString& primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
  2609. const wxString& secondaryName = aSubgraph->GetNameForDriver( driver );
  2610. if( primaryName == secondaryName )
  2611. continue;
  2612. wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
  2613. "items; %s will be used in the netlist" ),
  2614. primaryName, secondaryName, primaryName );
  2615. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
  2616. ercItem->SetItems( aSubgraph->m_driver, driver );
  2617. ercItem->SetSheetSpecificPath( aSubgraph->GetSheet() );
  2618. ercItem->SetItemsSheetPaths( aSubgraph->GetSheet(), aSubgraph->m_sheet );
  2619. ercItem->SetErrorMessage( msg );
  2620. SCH_MARKER* marker = new SCH_MARKER( ercItem, driver->GetPosition() );
  2621. aSubgraph->m_sheet.LastScreen()->Append( marker );
  2622. return false;
  2623. }
  2624. }
  2625. }
  2626. return true;
  2627. }
  2628. bool CONNECTION_GRAPH::ercCheckNetclassConflicts( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
  2629. {
  2630. wxString firstNetclass;
  2631. SCH_ITEM* firstNetclassDriver = nullptr;
  2632. const SCH_SHEET_PATH* firstNetclassDriverSheet = nullptr;
  2633. bool conflictFound = false;
  2634. for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
  2635. {
  2636. for( SCH_ITEM* item : subgraph->m_items )
  2637. {
  2638. const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProvider =
  2639. subgraph->GetNetclassesForDriver( item, true );
  2640. if( netclassesWithProvider.size() == 0 )
  2641. continue;
  2642. auto checkNetclass = [&]( const std::pair<wxString, SCH_ITEM*>& netclass )
  2643. {
  2644. if( netclass.first != firstNetclass )
  2645. {
  2646. if( !firstNetclassDriver )
  2647. {
  2648. firstNetclass = netclass.first;
  2649. firstNetclassDriver = netclass.second;
  2650. firstNetclassDriverSheet = &subgraph->GetSheet();
  2651. }
  2652. else
  2653. {
  2654. conflictFound = true;
  2655. std::shared_ptr<ERC_ITEM> ercItem =
  2656. ERC_ITEM::Create( ERCE_NETCLASS_CONFLICT );
  2657. ercItem->SetItems( firstNetclassDriver, netclass.second );
  2658. ercItem->SetSheetSpecificPath( subgraph->GetSheet() );
  2659. ercItem->SetItemsSheetPaths( *firstNetclassDriverSheet,
  2660. subgraph->GetSheet() );
  2661. SCH_MARKER* marker =
  2662. new SCH_MARKER( ercItem, netclass.second->GetPosition() );
  2663. subgraph->m_sheet.LastScreen()->Append( marker );
  2664. }
  2665. }
  2666. };
  2667. for( const std::pair<wxString, SCH_ITEM*>& netclass : netclassesWithProvider )
  2668. checkNetclass( netclass );
  2669. }
  2670. }
  2671. return conflictFound;
  2672. }
  2673. bool CONNECTION_GRAPH::ercCheckBusToNetConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  2674. {
  2675. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  2676. SCH_SCREEN* screen = sheet.LastScreen();
  2677. SCH_ITEM* net_item = nullptr;
  2678. SCH_ITEM* bus_item = nullptr;
  2679. SCH_CONNECTION conn( this );
  2680. for( SCH_ITEM* item : aSubgraph->m_items )
  2681. {
  2682. switch( item->Type() )
  2683. {
  2684. case SCH_LINE_T:
  2685. {
  2686. if( item->GetLayer() == LAYER_BUS )
  2687. bus_item = ( !bus_item ) ? item : bus_item;
  2688. else
  2689. net_item = ( !net_item ) ? item : net_item;
  2690. break;
  2691. }
  2692. case SCH_LABEL_T:
  2693. case SCH_GLOBAL_LABEL_T:
  2694. case SCH_SHEET_PIN_T:
  2695. case SCH_HIER_LABEL_T:
  2696. {
  2697. SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
  2698. conn.ConfigureFromLabel( EscapeString( text->GetShownText( &sheet, false ),
  2699. CTX_NETNAME ) );
  2700. if( conn.IsBus() )
  2701. bus_item = ( !bus_item ) ? item : bus_item;
  2702. else
  2703. net_item = ( !net_item ) ? item : net_item;
  2704. break;
  2705. }
  2706. default:
  2707. break;
  2708. }
  2709. }
  2710. if( net_item && bus_item )
  2711. {
  2712. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
  2713. ercItem->SetSheetSpecificPath( sheet );
  2714. ercItem->SetItems( net_item, bus_item );
  2715. SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
  2716. screen->Append( marker );
  2717. return false;
  2718. }
  2719. return true;
  2720. }
  2721. bool CONNECTION_GRAPH::ercCheckBusToBusConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  2722. {
  2723. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  2724. SCH_SCREEN* screen = sheet.LastScreen();
  2725. SCH_ITEM* label = nullptr;
  2726. SCH_ITEM* port = nullptr;
  2727. for( SCH_ITEM* item : aSubgraph->m_items )
  2728. {
  2729. switch( item->Type() )
  2730. {
  2731. case SCH_TEXT_T:
  2732. case SCH_GLOBAL_LABEL_T:
  2733. {
  2734. if( !label && item->Connection( &sheet )->IsBus() )
  2735. label = item;
  2736. break;
  2737. }
  2738. case SCH_SHEET_PIN_T:
  2739. case SCH_HIER_LABEL_T:
  2740. {
  2741. if( !port && item->Connection( &sheet )->IsBus() )
  2742. port = item;
  2743. break;
  2744. }
  2745. default:
  2746. break;
  2747. }
  2748. }
  2749. if( label && port )
  2750. {
  2751. bool match = false;
  2752. for( const auto& member : label->Connection( &sheet )->Members() )
  2753. {
  2754. for( const auto& test : port->Connection( &sheet )->Members() )
  2755. {
  2756. if( test != member && member->Name() == test->Name() )
  2757. {
  2758. match = true;
  2759. break;
  2760. }
  2761. }
  2762. if( match )
  2763. break;
  2764. }
  2765. if( !match )
  2766. {
  2767. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
  2768. ercItem->SetSheetSpecificPath( sheet );
  2769. ercItem->SetItems( label, port );
  2770. SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
  2771. screen->Append( marker );
  2772. return false;
  2773. }
  2774. }
  2775. return true;
  2776. }
  2777. bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  2778. {
  2779. bool conflict = false;
  2780. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  2781. SCH_SCREEN* screen = sheet.LastScreen();
  2782. SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
  2783. SCH_ITEM* bus_wire = nullptr;
  2784. wxString bus_name;
  2785. if( !aSubgraph->m_driver_connection )
  2786. {
  2787. // Incomplete bus entry. Let the unconnected tests handle it.
  2788. return true;
  2789. }
  2790. for( SCH_ITEM* item : aSubgraph->m_items )
  2791. {
  2792. switch( item->Type() )
  2793. {
  2794. case SCH_BUS_WIRE_ENTRY_T:
  2795. {
  2796. if( !bus_entry )
  2797. bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  2798. break;
  2799. }
  2800. default:
  2801. break;
  2802. }
  2803. }
  2804. if( bus_entry && bus_entry->m_connected_bus_item )
  2805. {
  2806. bus_wire = bus_entry->m_connected_bus_item;
  2807. wxASSERT( bus_wire->Type() == SCH_LINE_T );
  2808. // In some cases, the connection list (SCH_CONNECTION*) can be null.
  2809. // Skip null connections.
  2810. if( bus_entry->Connection( &sheet )
  2811. && bus_wire->Type() == SCH_LINE_T
  2812. && bus_wire->Connection( &sheet ) )
  2813. {
  2814. conflict = true; // Assume a conflict; we'll reset if we find it's OK
  2815. bus_name = bus_wire->Connection( &sheet )->Name();
  2816. std::set<wxString> test_names;
  2817. test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
  2818. wxString baseName = sheet.PathHumanReadable();
  2819. for( SCH_ITEM* driver : aSubgraph->m_drivers )
  2820. test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
  2821. for( const auto& member : bus_wire->Connection( &sheet )->Members() )
  2822. {
  2823. if( member->Type() == CONNECTION_TYPE::BUS )
  2824. {
  2825. for( const auto& sub_member : member->Members() )
  2826. {
  2827. if( test_names.count( sub_member->FullLocalName() ) )
  2828. conflict = false;
  2829. }
  2830. }
  2831. else if( test_names.count( member->FullLocalName() ) )
  2832. {
  2833. conflict = false;
  2834. }
  2835. }
  2836. }
  2837. }
  2838. // Don't report warnings if this bus member has been overridden by a higher priority power pin
  2839. // or global label
  2840. if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
  2841. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  2842. {
  2843. conflict = false;
  2844. }
  2845. if( conflict )
  2846. {
  2847. wxString netName = aSubgraph->m_driver_connection->Name();
  2848. wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
  2849. " member of that bus" ),
  2850. UnescapeString( netName ),
  2851. UnescapeString( bus_name ) );
  2852. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
  2853. ercItem->SetSheetSpecificPath( sheet );
  2854. ercItem->SetItems( bus_entry, bus_wire );
  2855. ercItem->SetErrorMessage( msg );
  2856. SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
  2857. screen->Append( marker );
  2858. return false;
  2859. }
  2860. return true;
  2861. }
  2862. bool CONNECTION_GRAPH::ercCheckNoConnects( const CONNECTION_SUBGRAPH* aSubgraph )
  2863. {
  2864. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  2865. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  2866. SCH_SCREEN* screen = sheet.LastScreen();
  2867. bool ok = true;
  2868. SCH_PIN* pin = nullptr;
  2869. std::set<SCH_PIN*> unique_pins;
  2870. std::set<SCH_LABEL_BASE*> unique_labels;
  2871. wxString netName = GetResolvedSubgraphName( aSubgraph );
  2872. auto process_subgraph = [&]( const CONNECTION_SUBGRAPH* aProcessGraph )
  2873. {
  2874. // Any subgraph that contains a no-connect should not
  2875. // more than one pin (which would indicate it is connected
  2876. for( SCH_ITEM* item : aProcessGraph->m_items )
  2877. {
  2878. switch( item->Type() )
  2879. {
  2880. case SCH_PIN_T:
  2881. {
  2882. SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
  2883. // Only link NC to pin on the current subgraph being checked
  2884. if( aProcessGraph == aSubgraph )
  2885. pin = test_pin;
  2886. if( std::none_of( unique_pins.begin(), unique_pins.end(),
  2887. [test_pin]( SCH_PIN* aPin )
  2888. {
  2889. return test_pin->IsStacked( aPin );
  2890. }
  2891. ))
  2892. {
  2893. unique_pins.insert( test_pin );
  2894. }
  2895. break;
  2896. }
  2897. case SCH_LABEL_T:
  2898. case SCH_GLOBAL_LABEL_T:
  2899. case SCH_HIER_LABEL_T:
  2900. unique_labels.insert( static_cast<SCH_LABEL_BASE*>( item ) );
  2901. KI_FALLTHROUGH;
  2902. default:
  2903. break;
  2904. }
  2905. }
  2906. };
  2907. auto it = m_net_name_to_subgraphs_map.find( netName );
  2908. if( it != m_net_name_to_subgraphs_map.end() )
  2909. {
  2910. for( const CONNECTION_SUBGRAPH* subgraph : it->second )
  2911. {
  2912. process_subgraph( subgraph );
  2913. }
  2914. }
  2915. else
  2916. {
  2917. process_subgraph( aSubgraph );
  2918. }
  2919. if( aSubgraph->m_no_connect != nullptr )
  2920. {
  2921. // Special case: If the subgraph being checked consists of only a hier port/pin and
  2922. // a no-connect, we don't issue a "no-connect connected" warning just because
  2923. // connections exist on the sheet on the other side of the link.
  2924. VECTOR2I noConnectPos = aSubgraph->m_no_connect->GetPosition();
  2925. for( SCH_SHEET_PIN* hierPin : aSubgraph->m_hier_pins )
  2926. {
  2927. if( hierPin->GetPosition() == noConnectPos )
  2928. return true;
  2929. }
  2930. for( SCH_HIERLABEL* hierLabel : aSubgraph->m_hier_ports )
  2931. {
  2932. if( hierLabel->GetPosition() == noConnectPos )
  2933. return true;
  2934. }
  2935. if( unique_pins.size() > 1 && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
  2936. {
  2937. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
  2938. ercItem->SetSheetSpecificPath( sheet );
  2939. ercItem->SetItemsSheetPaths( sheet );
  2940. VECTOR2I pos;
  2941. if( pin )
  2942. {
  2943. ercItem->SetItems( pin, aSubgraph->m_no_connect );
  2944. pos = pin->GetPosition();
  2945. }
  2946. else
  2947. {
  2948. ercItem->SetItems( aSubgraph->m_no_connect );
  2949. pos = aSubgraph->m_no_connect->GetPosition();
  2950. }
  2951. SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
  2952. screen->Append( marker );
  2953. ok = false;
  2954. }
  2955. if( unique_pins.empty() && unique_labels.empty() &&
  2956. settings.IsTestEnabled( ERCE_NOCONNECT_NOT_CONNECTED ) )
  2957. {
  2958. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
  2959. ercItem->SetItems( aSubgraph->m_no_connect );
  2960. ercItem->SetSheetSpecificPath( sheet );
  2961. ercItem->SetItemsSheetPaths( sheet );
  2962. SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
  2963. screen->Append( marker );
  2964. ok = false;
  2965. }
  2966. }
  2967. else
  2968. {
  2969. bool has_other_connections = false;
  2970. std::vector<SCH_PIN*> pins;
  2971. // Any subgraph that lacks a no-connect and contains a pin should also
  2972. // contain at least one other potential driver
  2973. for( SCH_ITEM* item : aSubgraph->m_items )
  2974. {
  2975. switch( item->Type() )
  2976. {
  2977. case SCH_PIN_T:
  2978. {
  2979. SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
  2980. // Stacked pins do not count as other connections but non-stacked pins do
  2981. if( !has_other_connections && !pins.empty() && !test_pin->GetParentSymbol()->IsPower() )
  2982. {
  2983. for( SCH_PIN* other_pin : pins )
  2984. {
  2985. if( !test_pin->IsStacked( other_pin ) )
  2986. {
  2987. has_other_connections = true;
  2988. break;
  2989. }
  2990. }
  2991. }
  2992. pins.emplace_back( static_cast<SCH_PIN*>( item ) );
  2993. break;
  2994. }
  2995. default:
  2996. if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
  2997. has_other_connections = true;
  2998. break;
  2999. }
  3000. }
  3001. // For many checks, we can just use the first pin
  3002. pin = pins.empty() ? nullptr : pins[0];
  3003. // But if there is a power pin, it might be connected elsewhere
  3004. for( SCH_PIN* test_pin : pins )
  3005. {
  3006. // Prefer the pin is part of a real component rather than some stray power symbol
  3007. // Or else we may fail walking connected components to a power symbol pin since we reject
  3008. // starting at a power symbol
  3009. if( test_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
  3010. && !test_pin->IsGlobalPower() )
  3011. {
  3012. pin = test_pin;
  3013. break;
  3014. }
  3015. }
  3016. // Check if power input pins connect to anything else via net name,
  3017. // but not for power symbols (with visible or legacy invisible pins).
  3018. // We want to throw unconnected errors for power symbols even if they are connected to other
  3019. // net items by name, because usually failing to connect them graphically is a mistake
  3020. if( pin && !has_other_connections
  3021. && !pin->IsGlobalPower()
  3022. && !pin->GetLibPin()->GetParentSymbol()->IsPower() )
  3023. {
  3024. wxString name = pin->Connection( &sheet )->Name();
  3025. wxString local_name = pin->Connection( &sheet )->Name( true );
  3026. if( m_global_label_cache.count( name )
  3027. || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
  3028. {
  3029. has_other_connections = true;
  3030. }
  3031. }
  3032. // Only one pin, and it's not a no-connect pin
  3033. if( pin && !has_other_connections
  3034. && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
  3035. && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
  3036. && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  3037. {
  3038. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  3039. ercItem->SetSheetSpecificPath( sheet );
  3040. ercItem->SetItemsSheetPaths( sheet );
  3041. ercItem->SetItems( pin );
  3042. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
  3043. screen->Append( marker );
  3044. ok = false;
  3045. }
  3046. // If there are multiple pins in this SG, they might be indirectly connected (by netname)
  3047. // rather than directly connected (by wires). We want to flag dangling pins even if they
  3048. // join nets with another pin, as it's often a mistake
  3049. if( pins.size() > 1 )
  3050. {
  3051. for( SCH_PIN* testPin : pins )
  3052. {
  3053. // We only apply this test to power symbols, because other symbols have
  3054. // pins that are meant to be dangling, but the power symbols have pins
  3055. // that are *not* meant to be dangling.
  3056. if( testPin->GetLibPin()->GetParentSymbol()->IsPower()
  3057. && testPin->ConnectedItems( sheet ).empty()
  3058. && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  3059. {
  3060. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  3061. ercItem->SetSheetSpecificPath( sheet );
  3062. ercItem->SetItemsSheetPaths( sheet );
  3063. ercItem->SetItems( testPin );
  3064. SCH_MARKER* marker = new SCH_MARKER( ercItem, testPin->GetPosition() );
  3065. screen->Append( marker );
  3066. ok = false;
  3067. }
  3068. }
  3069. }
  3070. }
  3071. return ok;
  3072. }
  3073. bool CONNECTION_GRAPH::ercCheckDanglingWireEndpoints( const CONNECTION_SUBGRAPH* aSubgraph )
  3074. {
  3075. int err_count = 0;
  3076. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  3077. for( SCH_ITEM* item : aSubgraph->m_items )
  3078. {
  3079. if( item->Type() != SCH_LINE_T || item->GetLayer() != LAYER_WIRE )
  3080. continue;
  3081. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  3082. if( line->IsGraphicLine() )
  3083. continue;
  3084. auto report_error = [&]( VECTOR2I& location )
  3085. {
  3086. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNCONNECTED_WIRE_ENDPOINT );
  3087. ercItem->SetItems( line );
  3088. ercItem->SetSheetSpecificPath( sheet );
  3089. ercItem->SetErrorMessage( _( "Unconnected wire endpoint" ) );
  3090. SCH_MARKER* marker = new SCH_MARKER( ercItem, location );
  3091. sheet.LastScreen()->Append( marker );
  3092. err_count++;
  3093. };
  3094. if( line->IsStartDangling() )
  3095. report_error( line->GetConnectionPoints()[0] );
  3096. if( line->IsEndDangling() )
  3097. report_error( line->GetConnectionPoints()[1] );
  3098. }
  3099. return err_count > 0;
  3100. }
  3101. bool CONNECTION_GRAPH::ercCheckFloatingWires( const CONNECTION_SUBGRAPH* aSubgraph )
  3102. {
  3103. if( aSubgraph->m_driver )
  3104. return true;
  3105. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  3106. std::vector<SCH_ITEM*> wires;
  3107. // We've gotten this far, so we know we have no valid driver. All we need to do is check
  3108. // for a wire that we can place the error on.
  3109. for( SCH_ITEM* item : aSubgraph->m_items )
  3110. {
  3111. if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
  3112. wires.emplace_back( item );
  3113. else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
  3114. wires.emplace_back( item );
  3115. }
  3116. if( !wires.empty() )
  3117. {
  3118. SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
  3119. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
  3120. ercItem->SetSheetSpecificPath( sheet );
  3121. ercItem->SetItems( wires[0],
  3122. wires.size() > 1 ? wires[1] : nullptr,
  3123. wires.size() > 2 ? wires[2] : nullptr,
  3124. wires.size() > 3 ? wires[3] : nullptr );
  3125. SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
  3126. screen->Append( marker );
  3127. return false;
  3128. }
  3129. return true;
  3130. }
  3131. bool CONNECTION_GRAPH::ercCheckLabels( const CONNECTION_SUBGRAPH* aSubgraph )
  3132. {
  3133. // Label connection rules:
  3134. // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
  3135. // Local labels are flagged if they don't connect to any pins and don't have a no-connect
  3136. // Global labels are flagged if they appear only once, don't connect to any local labels,
  3137. // and don't have a no-connect marker
  3138. if( !aSubgraph->m_driver_connection )
  3139. return true;
  3140. // Buses are excluded from this test: many users create buses with only a single instance
  3141. // and it's not really a problem as long as the nets in the bus pass ERC
  3142. if( aSubgraph->m_driver_connection->IsBus() )
  3143. return true;
  3144. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  3145. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  3146. bool ok = true;
  3147. size_t pinCount = 0;
  3148. bool has_nc = !!aSubgraph->m_no_connect;
  3149. std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
  3150. auto hasPins =
  3151. []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> size_t
  3152. {
  3153. return std::count_if( aLocSubgraph->m_items.begin(), aLocSubgraph->m_items.end(),
  3154. []( const SCH_ITEM* item )
  3155. {
  3156. return item->Type() == SCH_PIN_T;
  3157. } );
  3158. };
  3159. auto reportError =
  3160. [&]( SCH_TEXT* aText, int errCode )
  3161. {
  3162. if( settings.IsTestEnabled( errCode ) )
  3163. {
  3164. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
  3165. ercItem->SetSheetSpecificPath( sheet );
  3166. ercItem->SetItems( aText );
  3167. SCH_MARKER* marker = new SCH_MARKER( ercItem, aText->GetPosition() );
  3168. aSubgraph->m_sheet.LastScreen()->Append( marker );
  3169. }
  3170. };
  3171. pinCount = hasPins( aSubgraph );
  3172. for( SCH_ITEM* item : aSubgraph->m_items )
  3173. {
  3174. switch( item->Type() )
  3175. {
  3176. case SCH_LABEL_T:
  3177. case SCH_GLOBAL_LABEL_T:
  3178. case SCH_HIER_LABEL_T:
  3179. {
  3180. SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
  3181. label_map[item->Type()].push_back( text );
  3182. // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
  3183. // we want to error if an individual label in the subgraph is floating, even if it's
  3184. // connected to other valid things by way of another label on the same sheet.
  3185. if( text->IsDangling() )
  3186. {
  3187. reportError( text, ERCE_LABEL_NOT_CONNECTED );
  3188. return false;
  3189. }
  3190. break;
  3191. }
  3192. default:
  3193. break;
  3194. }
  3195. }
  3196. if( label_map.empty() )
  3197. return true;
  3198. // No-connects on net neighbors will be noticed before, but to notice them on bus parents we
  3199. // need to walk the graph
  3200. for( auto& [ connection, subgraphs ] : aSubgraph->m_bus_parents )
  3201. {
  3202. for( CONNECTION_SUBGRAPH* busParent : subgraphs )
  3203. {
  3204. if( busParent->m_no_connect )
  3205. {
  3206. has_nc = true;
  3207. break;
  3208. }
  3209. CONNECTION_SUBGRAPH* hp = busParent->m_hier_parent;
  3210. while( hp )
  3211. {
  3212. if( hp->m_no_connect )
  3213. {
  3214. has_nc = true;
  3215. break;
  3216. }
  3217. hp = hp->m_hier_parent;
  3218. }
  3219. }
  3220. }
  3221. wxString netName = GetResolvedSubgraphName( aSubgraph );
  3222. wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" ) );
  3223. // Labels that have multiple pins connected are not dangling (may be used for naming segments)
  3224. // so leave them without errors here
  3225. if( pinCount > 1 )
  3226. return true;
  3227. for( auto& [type, label_vec] : label_map )
  3228. {
  3229. switch( type )
  3230. {
  3231. case SCH_GLOBAL_LABEL_T:
  3232. if( !settings.IsTestEnabled( ERCE_GLOBLABEL ) )
  3233. continue;
  3234. break;
  3235. default:
  3236. if( !settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
  3237. continue;
  3238. break;
  3239. }
  3240. for( SCH_TEXT* text : label_vec )
  3241. {
  3242. size_t allPins = pinCount;
  3243. auto it = m_net_name_to_subgraphs_map.find( netName );
  3244. if( it != m_net_name_to_subgraphs_map.end() )
  3245. {
  3246. for( const CONNECTION_SUBGRAPH* neighbor : it->second )
  3247. {
  3248. if( neighbor == aSubgraph )
  3249. continue;
  3250. if( neighbor->m_no_connect )
  3251. has_nc = true;
  3252. allPins += hasPins( neighbor );
  3253. }
  3254. }
  3255. if( allPins == 1 && !has_nc )
  3256. {
  3257. reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL
  3258. : ERCE_LABEL_NOT_CONNECTED );
  3259. ok = false;
  3260. }
  3261. if( allPins == 0 )
  3262. {
  3263. reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL
  3264. : ERCE_LABEL_NOT_CONNECTED );
  3265. ok = false;
  3266. }
  3267. }
  3268. }
  3269. return ok;
  3270. }
  3271. int CONNECTION_GRAPH::ercCheckSingleGlobalLabel()
  3272. {
  3273. int errors = 0;
  3274. std::map<wxString, std::tuple<int, const SCH_ITEM*, SCH_SHEET_PATH>> labelData;
  3275. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  3276. {
  3277. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
  3278. {
  3279. SCH_TEXT* labelText = static_cast<SCH_TEXT*>( item );
  3280. wxString resolvedLabelText =
  3281. EscapeString( labelText->GetShownText( &sheet, false ), CTX_NETNAME );
  3282. if( labelData.find( resolvedLabelText ) == labelData.end() )
  3283. {
  3284. labelData[resolvedLabelText] = { 1, item, sheet };
  3285. }
  3286. else
  3287. {
  3288. std::get<0>( labelData[resolvedLabelText] ) += 1;
  3289. std::get<1>( labelData[resolvedLabelText] ) = nullptr;
  3290. std::get<2>( labelData[resolvedLabelText] ) = sheet;
  3291. }
  3292. }
  3293. }
  3294. for( const auto& label : labelData )
  3295. {
  3296. if( std::get<0>( label.second ) == 1 )
  3297. {
  3298. const SCH_SHEET_PATH& sheet = std::get<2>( label.second );
  3299. const SCH_ITEM* item = std::get<1>( label.second );
  3300. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SINGLE_GLOBAL_LABEL );
  3301. ercItem->SetItems( std::get<1>( label.second ) );
  3302. ercItem->SetSheetSpecificPath( sheet );
  3303. ercItem->SetItemsSheetPaths( sheet );
  3304. SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
  3305. sheet.LastScreen()->Append( marker );
  3306. errors++;
  3307. }
  3308. }
  3309. return errors;
  3310. }
  3311. int CONNECTION_GRAPH::ercCheckHierSheets()
  3312. {
  3313. int errors = 0;
  3314. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  3315. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  3316. {
  3317. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
  3318. {
  3319. SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
  3320. SCH_SHEET_PATH parentSheetPath = sheet;
  3321. parentSheetPath.push_back( parentSheet );
  3322. std::map<wxString, SCH_SHEET_PIN*> pins;
  3323. std::map<wxString, SCH_HIERLABEL*> labels;
  3324. for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
  3325. {
  3326. if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
  3327. pins[ pin->GetShownText( &parentSheetPath, false ) ] = pin;
  3328. if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  3329. {
  3330. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  3331. ercItem->SetItems( pin );
  3332. ercItem->SetSheetSpecificPath( sheet );
  3333. ercItem->SetItemsSheetPaths( sheet );
  3334. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
  3335. sheet.LastScreen()->Append( marker );
  3336. errors++;
  3337. }
  3338. }
  3339. if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
  3340. {
  3341. std::set<wxString> matchedPins;
  3342. for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
  3343. {
  3344. if( subItem->Type() == SCH_HIER_LABEL_T )
  3345. {
  3346. SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
  3347. wxString labelText = label->GetShownText( &parentSheetPath, false );
  3348. if( !pins.count( labelText ) )
  3349. labels[ labelText ] = label;
  3350. else
  3351. matchedPins.insert( labelText );
  3352. }
  3353. }
  3354. for( const wxString& matched : matchedPins )
  3355. pins.erase( matched );
  3356. for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
  3357. {
  3358. wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
  3359. "label inside the sheet" ),
  3360. UnescapeString( unmatched.first ) );
  3361. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
  3362. ercItem->SetItems( unmatched.second );
  3363. ercItem->SetErrorMessage( msg );
  3364. ercItem->SetSheetSpecificPath( sheet );
  3365. ercItem->SetItemsSheetPaths( sheet );
  3366. SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
  3367. sheet.LastScreen()->Append( marker );
  3368. errors++;
  3369. }
  3370. for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
  3371. {
  3372. wxString msg = wxString::Format( _( "Hierarchical label %s has no matching "
  3373. "sheet pin in the parent sheet" ),
  3374. UnescapeString( unmatched.first ) );
  3375. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
  3376. ercItem->SetItems( unmatched.second );
  3377. ercItem->SetErrorMessage( msg );
  3378. ercItem->SetSheetSpecificPath( parentSheetPath );
  3379. ercItem->SetItemsSheetPaths( parentSheetPath );
  3380. SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
  3381. parentSheet->GetScreen()->Append( marker );
  3382. errors++;
  3383. }
  3384. }
  3385. }
  3386. }
  3387. return errors;
  3388. }