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.

4356 lines
148 KiB

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