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.

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