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.

2744 lines
92 KiB

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