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.

2756 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. CONNECTION_SUBGRAPH* 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. SCH_CONNECTION* 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( SCH_ITEM* connected_item : members )
  612. {
  613. if( connected_item->Type() == SCH_NO_CONNECT_T )
  614. subgraph->m_no_connect = connected_item;
  615. SCH_CONNECTION* 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. // Was this neighbor already updated from a different sheet? Don't rename it again
  1381. if( neighbor_conn->Sheet() != neighbor->m_sheet )
  1382. continue;
  1383. // Safety check against infinite recursion
  1384. wxASSERT( neighbor_conn->IsNet() );
  1385. wxLogTrace( ConnTrace, "%lu (%s) connected to bus member %s (local %s)",
  1386. neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
  1387. // Take whichever name is higher priority
  1388. if( CONNECTION_SUBGRAPH::GetDriverPriority( neighbor->m_driver )
  1389. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  1390. {
  1391. member->Clone( *neighbor_conn );
  1392. stale_bus_members.push_back( member );
  1393. }
  1394. else
  1395. {
  1396. neighbor_conn->Clone( *member );
  1397. neighbor->UpdateItemConnections();
  1398. recacheSubgraphName( neighbor, neighbor_name );
  1399. // Recurse onto this neighbor in case it needs to re-propagate
  1400. neighbor->m_dirty = true;
  1401. propagateToNeighbors( neighbor );
  1402. }
  1403. }
  1404. }
  1405. };
  1406. // If we are a bus, we must propagate to local neighbors and then the hierarchy
  1407. if( conn->IsBus() )
  1408. propagate_bus_neighbors( aSubgraph );
  1409. // If we don't have any hier pins (i.e. no children), nothing to do
  1410. if( aSubgraph->m_hier_pins.empty() )
  1411. {
  1412. // If we also don't have any parents, we'll never be visited again
  1413. if( aSubgraph->m_hier_ports.empty() )
  1414. aSubgraph->m_dirty = false;
  1415. return;
  1416. }
  1417. // If we do have hier ports, skip this subgraph as it will be visited by a parent
  1418. // TODO(JE) this will leave the subgraph dirty if there is no matching parent subgraph,
  1419. // which should be flagged as an ERC error
  1420. if( !aSubgraph->m_hier_ports.empty() )
  1421. return;
  1422. visited.insert( aSubgraph );
  1423. wxLogTrace( ConnTrace, "Propagating %lu (%s) to subsheets",
  1424. aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
  1425. visit( aSubgraph );
  1426. for( unsigned i = 0; i < search_list.size(); i++ )
  1427. {
  1428. auto child = search_list[i];
  1429. visited.insert( child );
  1430. visit( child );
  1431. child->m_dirty = false;
  1432. }
  1433. // Now, find the best driver for this chain of subgraphs
  1434. CONNECTION_SUBGRAPH* driver = aSubgraph;
  1435. CONNECTION_SUBGRAPH::PRIORITY highest =
  1436. CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver );
  1437. // Check if a subsheet has a higher-priority connection to the same net
  1438. if( highest < CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  1439. {
  1440. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1441. {
  1442. CONNECTION_SUBGRAPH::PRIORITY priority =
  1443. CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
  1444. // Upgrade driver to be this subgraph if this subgraph has a power pin or global
  1445. // Also upgrade if we found something with a shorter sheet path (higher in hierarchy)
  1446. // but with an equivalent priority
  1447. if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
  1448. ( priority >= highest && subgraph->m_sheet.size() < aSubgraph->m_sheet.size() ) )
  1449. driver = subgraph;
  1450. }
  1451. }
  1452. if( driver != aSubgraph )
  1453. {
  1454. wxLogTrace( ConnTrace, "%lu (%s) overridden by new driver %lu (%s)",
  1455. aSubgraph->m_code, aSubgraph->m_driver_connection->Name(),
  1456. driver->m_code, driver->m_driver_connection->Name() );
  1457. }
  1458. conn = driver->m_driver_connection;
  1459. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1460. {
  1461. wxString old_name = subgraph->m_driver_connection->Name();
  1462. subgraph->m_driver_connection->Clone( *conn );
  1463. subgraph->UpdateItemConnections();
  1464. if( old_name != conn->Name() )
  1465. recacheSubgraphName( subgraph, old_name );
  1466. if( conn->IsBus() )
  1467. propagate_bus_neighbors( subgraph );
  1468. }
  1469. // Somewhere along the way, a bus member may have been upgraded to a global or power label.
  1470. // Because this can happen anywhere, we need a second pass to update all instances of that bus
  1471. // member to have the correct connection info
  1472. if( conn->IsBus() && !stale_bus_members.empty() )
  1473. {
  1474. for( auto stale_member : stale_bus_members )
  1475. {
  1476. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1477. {
  1478. SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
  1479. stale_member );
  1480. wxASSERT( member );
  1481. wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", subgraph->m_code,
  1482. subgraph->m_driver_connection->Name(), member->LocalName(),
  1483. stale_member->Name() );
  1484. member->Clone( *stale_member );
  1485. propagate_bus_neighbors( subgraph );
  1486. }
  1487. }
  1488. }
  1489. aSubgraph->m_dirty = false;
  1490. }
  1491. std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
  1492. CONNECTION_SUBGRAPH* aSubgraph )
  1493. {
  1494. auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
  1495. switch( aItem->Type() )
  1496. {
  1497. case SCH_PIN_T:
  1498. {
  1499. auto pin = static_cast<SCH_PIN*>( aItem );
  1500. if( pin->IsPowerConnection() )
  1501. c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
  1502. break;
  1503. }
  1504. case SCH_GLOBAL_LABEL_T:
  1505. case SCH_HIER_LABEL_T:
  1506. case SCH_LABEL_T:
  1507. {
  1508. c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
  1509. break;
  1510. }
  1511. default:
  1512. break;
  1513. }
  1514. if( c )
  1515. {
  1516. c->SetGraph( this );
  1517. c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
  1518. }
  1519. return c;
  1520. }
  1521. SCH_CONNECTION* CONNECTION_GRAPH::matchBusMember( SCH_CONNECTION* aBusConnection,
  1522. SCH_CONNECTION* aSearch )
  1523. {
  1524. wxASSERT( aBusConnection->IsBus() );
  1525. SCH_CONNECTION* match = nullptr;
  1526. if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
  1527. {
  1528. // Vector bus: compare against index, because we allow the name
  1529. // to be different
  1530. for( const auto& bus_member : aBusConnection->Members() )
  1531. {
  1532. if( bus_member->VectorIndex() == aSearch->VectorIndex() )
  1533. {
  1534. match = bus_member.get();
  1535. break;
  1536. }
  1537. }
  1538. }
  1539. else
  1540. {
  1541. // Group bus
  1542. for( const auto& c : aBusConnection->Members() )
  1543. {
  1544. // Vector inside group: compare names, because for bus groups
  1545. // we expect the naming to be consistent across all usages
  1546. // TODO(JE) explain this in the docs
  1547. if( c->Type() == CONNECTION_TYPE::BUS )
  1548. {
  1549. for( const auto& bus_member : c->Members() )
  1550. {
  1551. if( bus_member->LocalName() == aSearch->LocalName() )
  1552. {
  1553. match = bus_member.get();
  1554. break;
  1555. }
  1556. }
  1557. }
  1558. else if( c->LocalName() == aSearch->LocalName() )
  1559. {
  1560. match = c.get();
  1561. break;
  1562. }
  1563. }
  1564. }
  1565. return match;
  1566. }
  1567. void CONNECTION_GRAPH::recacheSubgraphName( CONNECTION_SUBGRAPH* aSubgraph,
  1568. const wxString& aOldName )
  1569. {
  1570. auto it = m_net_name_to_subgraphs_map.find( aOldName );
  1571. if( it != m_net_name_to_subgraphs_map.end() )
  1572. {
  1573. std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
  1574. vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
  1575. }
  1576. wxLogTrace( ConnTrace, "recacheSubgraphName: %s => %s", aOldName,
  1577. aSubgraph->m_driver_connection->Name() );
  1578. m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
  1579. }
  1580. std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
  1581. {
  1582. auto it = m_bus_alias_cache.find( aName );
  1583. return it != m_bus_alias_cache.end() ? it->second : nullptr;
  1584. }
  1585. std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
  1586. {
  1587. std::vector<const CONNECTION_SUBGRAPH*> ret;
  1588. for( auto&& subgraph : m_subgraphs )
  1589. {
  1590. // Graph is supposed to be up-to-date before calling this
  1591. wxASSERT( !subgraph->m_dirty );
  1592. if( !subgraph->m_driver )
  1593. continue;
  1594. auto sheet = subgraph->m_sheet;
  1595. auto connection = subgraph->m_driver->Connection( &sheet );
  1596. if( !connection->IsBus() )
  1597. continue;
  1598. auto labels = subgraph->GetBusLabels();
  1599. if( labels.size() > 1 )
  1600. {
  1601. bool different = false;
  1602. wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
  1603. for( unsigned i = 1; i < labels.size(); ++i )
  1604. {
  1605. if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
  1606. {
  1607. different = true;
  1608. break;
  1609. }
  1610. }
  1611. if( !different )
  1612. continue;
  1613. wxLogTrace( ConnTrace, "SG %ld (%s) has multiple bus labels", subgraph->m_code,
  1614. connection->Name() );
  1615. ret.push_back( subgraph );
  1616. }
  1617. }
  1618. return ret;
  1619. }
  1620. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::FindSubgraphByName( const wxString& aNetName,
  1621. const SCH_SHEET_PATH& aPath )
  1622. {
  1623. auto it = m_net_name_to_subgraphs_map.find( aNetName );
  1624. if( it == m_net_name_to_subgraphs_map.end() )
  1625. return nullptr;
  1626. for( CONNECTION_SUBGRAPH* sg : it->second )
  1627. {
  1628. // Cache is supposed to be valid by now
  1629. wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
  1630. if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
  1631. return sg;
  1632. }
  1633. return nullptr;
  1634. }
  1635. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::FindFirstSubgraphByName( const wxString& aNetName )
  1636. {
  1637. auto it = m_net_name_to_subgraphs_map.find( aNetName );
  1638. if( it == m_net_name_to_subgraphs_map.end() )
  1639. return nullptr;
  1640. wxASSERT( !it->second.empty() );
  1641. return it->second[0];
  1642. }
  1643. CONNECTION_SUBGRAPH* CONNECTION_GRAPH::GetSubgraphForItem( SCH_ITEM* aItem )
  1644. {
  1645. auto it = m_item_to_subgraph_map.find( aItem );
  1646. CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
  1647. while( ret && ret->m_absorbed )
  1648. ret = ret->m_absorbed_by;
  1649. return ret;
  1650. }
  1651. int CONNECTION_GRAPH::RunERC()
  1652. {
  1653. int error_count = 0;
  1654. wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
  1655. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  1656. for( auto&& subgraph : m_subgraphs )
  1657. {
  1658. // Graph is supposed to be up-to-date before calling RunERC()
  1659. wxASSERT( !subgraph->m_dirty );
  1660. if( subgraph->m_absorbed )
  1661. continue;
  1662. /**
  1663. * NOTE:
  1664. *
  1665. * We could check that labels attached to bus subgraphs follow the
  1666. * proper format (i.e. actually define a bus).
  1667. *
  1668. * This check doesn't need to be here right now because labels
  1669. * won't actually be connected to bus wires if they aren't in the right
  1670. * format due to their TestDanglingEnds() implementation.
  1671. */
  1672. if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
  1673. {
  1674. if( !subgraph->ResolveDrivers( true ) )
  1675. error_count++;
  1676. }
  1677. else
  1678. subgraph->ResolveDrivers( false );
  1679. if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
  1680. {
  1681. if( !ercCheckBusToNetConflicts( subgraph ) )
  1682. error_count++;
  1683. }
  1684. if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
  1685. {
  1686. if( !ercCheckBusToBusEntryConflicts( subgraph ) )
  1687. error_count++;
  1688. }
  1689. if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
  1690. {
  1691. if( !ercCheckBusToBusConflicts( subgraph ) )
  1692. error_count++;
  1693. }
  1694. if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
  1695. {
  1696. if( !ercCheckFloatingWires( subgraph ) )
  1697. error_count++;
  1698. }
  1699. // The following checks are always performed since they don't currently
  1700. // have an option exposed to the user
  1701. if( !ercCheckNoConnects( subgraph ) )
  1702. error_count++;
  1703. if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
  1704. || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
  1705. {
  1706. if( !ercCheckLabels( subgraph ) )
  1707. error_count++;
  1708. }
  1709. }
  1710. // Hierarchical sheet checking is done at the schematic level
  1711. if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
  1712. error_count += ercCheckHierSheets();
  1713. return error_count;
  1714. }
  1715. bool CONNECTION_GRAPH::ercCheckBusToNetConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  1716. {
  1717. auto sheet = aSubgraph->m_sheet;
  1718. auto screen = sheet.LastScreen();
  1719. SCH_ITEM* net_item = nullptr;
  1720. SCH_ITEM* bus_item = nullptr;
  1721. SCH_CONNECTION conn( this );
  1722. for( auto item : aSubgraph->m_items )
  1723. {
  1724. switch( item->Type() )
  1725. {
  1726. case SCH_LINE_T:
  1727. {
  1728. if( item->GetLayer() == LAYER_BUS )
  1729. bus_item = ( !bus_item ) ? item : bus_item;
  1730. else
  1731. net_item = ( !net_item ) ? item : net_item;
  1732. break;
  1733. }
  1734. case SCH_GLOBAL_LABEL_T:
  1735. case SCH_SHEET_PIN_T:
  1736. case SCH_HIER_LABEL_T:
  1737. {
  1738. SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
  1739. conn.ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
  1740. if( conn.IsBus() )
  1741. bus_item = ( !bus_item ) ? item : bus_item;
  1742. else
  1743. net_item = ( !net_item ) ? item : net_item;
  1744. break;
  1745. }
  1746. default:
  1747. break;
  1748. }
  1749. }
  1750. if( net_item && bus_item )
  1751. {
  1752. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
  1753. ercItem->SetItems( net_item, bus_item );
  1754. SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
  1755. screen->Append( marker );
  1756. return false;
  1757. }
  1758. return true;
  1759. }
  1760. bool CONNECTION_GRAPH::ercCheckBusToBusConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  1761. {
  1762. wxString msg;
  1763. auto sheet = aSubgraph->m_sheet;
  1764. auto screen = sheet.LastScreen();
  1765. SCH_ITEM* label = nullptr;
  1766. SCH_ITEM* port = nullptr;
  1767. for( auto item : aSubgraph->m_items )
  1768. {
  1769. switch( item->Type() )
  1770. {
  1771. case SCH_TEXT_T:
  1772. case SCH_GLOBAL_LABEL_T:
  1773. {
  1774. if( !label && item->Connection( &sheet )->IsBus() )
  1775. label = item;
  1776. break;
  1777. }
  1778. case SCH_SHEET_PIN_T:
  1779. case SCH_HIER_LABEL_T:
  1780. {
  1781. if( !port && item->Connection( &sheet )->IsBus() )
  1782. port = item;
  1783. break;
  1784. }
  1785. default:
  1786. break;
  1787. }
  1788. }
  1789. if( label && port )
  1790. {
  1791. bool match = false;
  1792. for( const auto& member : label->Connection( &sheet )->Members() )
  1793. {
  1794. for( const auto& test : port->Connection( &sheet )->Members() )
  1795. {
  1796. if( test != member && member->Name() == test->Name() )
  1797. {
  1798. match = true;
  1799. break;
  1800. }
  1801. }
  1802. if( match )
  1803. break;
  1804. }
  1805. if( !match )
  1806. {
  1807. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
  1808. ercItem->SetItems( label, port );
  1809. SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
  1810. screen->Append( marker );
  1811. return false;
  1812. }
  1813. }
  1814. return true;
  1815. }
  1816. bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( const CONNECTION_SUBGRAPH* aSubgraph )
  1817. {
  1818. bool conflict = false;
  1819. auto sheet = aSubgraph->m_sheet;
  1820. auto screen = sheet.LastScreen();
  1821. SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
  1822. SCH_ITEM* bus_wire = nullptr;
  1823. wxString bus_name;
  1824. for( auto item : aSubgraph->m_items )
  1825. {
  1826. switch( item->Type() )
  1827. {
  1828. case SCH_BUS_WIRE_ENTRY_T:
  1829. {
  1830. if( !bus_entry )
  1831. bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  1832. break;
  1833. }
  1834. default:
  1835. break;
  1836. }
  1837. }
  1838. if( bus_entry && bus_entry->m_connected_bus_item )
  1839. {
  1840. bus_wire = bus_entry->m_connected_bus_item;
  1841. wxASSERT( bus_wire->Type() == SCH_LINE_T );
  1842. // In some cases, the connection list (SCH_CONNECTION*) can be null.
  1843. // Skip null connections.
  1844. if( bus_entry->Connection( &sheet )
  1845. && bus_wire->Type() == SCH_LINE_T
  1846. && bus_wire->Connection( &sheet ) )
  1847. {
  1848. conflict = true; // Assume a conflict; we'll reset if we find it's OK
  1849. bus_name = bus_wire->Connection( &sheet )->Name( true );
  1850. wxString test_name = bus_entry->Connection( &sheet )->Name( true );
  1851. for( const auto& member : bus_wire->Connection( &sheet )->Members() )
  1852. {
  1853. if( member->Type() == CONNECTION_TYPE::BUS )
  1854. {
  1855. for( const auto& sub_member : member->Members() )
  1856. {
  1857. if( sub_member->Name( true ) == test_name )
  1858. conflict = false;
  1859. }
  1860. }
  1861. else if( member->Name( true ) == test_name )
  1862. {
  1863. conflict = false;
  1864. }
  1865. }
  1866. }
  1867. }
  1868. // Don't report warnings if this bus member has been overridden by a higher priority power pin
  1869. // or global label
  1870. if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
  1871. >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN )
  1872. conflict = false;
  1873. if( conflict )
  1874. {
  1875. wxString netName = aSubgraph->m_driver_connection->Name( true );
  1876. wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
  1877. " member of that bus" ),
  1878. UnescapeString( netName ),
  1879. UnescapeString( bus_name ) );
  1880. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
  1881. ercItem->SetItems( bus_entry, bus_wire );
  1882. ercItem->SetErrorMessage( msg );
  1883. SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
  1884. screen->Append( marker );
  1885. return false;
  1886. }
  1887. return true;
  1888. }
  1889. // TODO(JE) Check sheet pins here too?
  1890. bool CONNECTION_GRAPH::ercCheckNoConnects( const CONNECTION_SUBGRAPH* aSubgraph )
  1891. {
  1892. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  1893. wxString msg;
  1894. const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
  1895. SCH_SCREEN* screen = sheet.LastScreen();
  1896. bool ok = true;
  1897. if( aSubgraph->m_no_connect != nullptr )
  1898. {
  1899. bool has_invalid_items = false;
  1900. bool has_other_items = false;
  1901. SCH_PIN* pin = nullptr;
  1902. std::vector<SCH_ITEM*> invalid_items;
  1903. wxPoint noConnectPos = aSubgraph->m_no_connect->GetPosition();
  1904. double minDist = 0;
  1905. // Any subgraph that contains both a pin and a no-connect should not
  1906. // contain any other driving items.
  1907. for( auto item : aSubgraph->m_items )
  1908. {
  1909. switch( item->Type() )
  1910. {
  1911. case SCH_PIN_T:
  1912. {
  1913. SCH_PIN* candidate = static_cast<SCH_PIN*>( item );
  1914. double dist = VECTOR2I( candidate->GetTransformedPosition() - noConnectPos )
  1915. .SquaredEuclideanNorm();
  1916. if( !pin || dist < minDist )
  1917. {
  1918. pin = candidate;
  1919. minDist = dist;
  1920. }
  1921. has_invalid_items |= has_other_items;
  1922. has_other_items = true;
  1923. break;
  1924. }
  1925. case SCH_LINE_T:
  1926. case SCH_JUNCTION_T:
  1927. case SCH_NO_CONNECT_T:
  1928. break;
  1929. default:
  1930. has_invalid_items = true;
  1931. has_other_items = true;
  1932. invalid_items.push_back( item );
  1933. }
  1934. }
  1935. if( pin && has_invalid_items && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
  1936. {
  1937. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
  1938. ercItem->SetItems( pin );
  1939. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
  1940. screen->Append( marker );
  1941. ok = false;
  1942. }
  1943. if( !has_other_items && settings.IsTestEnabled(ERCE_NOCONNECT_NOT_CONNECTED ) )
  1944. {
  1945. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
  1946. ercItem->SetItems( aSubgraph->m_no_connect );
  1947. SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
  1948. screen->Append( marker );
  1949. ok = false;
  1950. }
  1951. }
  1952. else
  1953. {
  1954. bool has_other_connections = false;
  1955. std::vector<SCH_PIN*> pins;
  1956. // Any subgraph that lacks a no-connect and contains a pin should also
  1957. // contain at least one other potential driver
  1958. for( SCH_ITEM* item : aSubgraph->m_items )
  1959. {
  1960. switch( item->Type() )
  1961. {
  1962. case SCH_PIN_T:
  1963. {
  1964. if( !pins.empty() )
  1965. has_other_connections = true;
  1966. pins.emplace_back( static_cast<SCH_PIN*>( item ) );
  1967. break;
  1968. }
  1969. default:
  1970. if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
  1971. has_other_connections = true;
  1972. break;
  1973. }
  1974. }
  1975. // For many checks, we can just use the first pin
  1976. SCH_PIN* pin = pins.empty() ? nullptr : pins[0];
  1977. // Check if invisible power input pins connect to anything else via net name,
  1978. // but not for power symbols as the ones in the standard library all have invisible pins
  1979. // and we want to throw unconnected errors for those even if they are connected to other
  1980. // net items by name, because usually failing to connect them graphically is a mistake
  1981. if( pin && !has_other_connections
  1982. && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
  1983. && !pin->IsVisible()
  1984. && !pin->GetLibPin()->GetParent()->IsPower() )
  1985. {
  1986. wxString name = pin->Connection( &sheet )->Name();
  1987. wxString local_name = pin->Connection( &sheet )->Name( true );
  1988. if( m_global_label_cache.count( name ) ||
  1989. ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
  1990. {
  1991. has_other_connections = true;
  1992. }
  1993. }
  1994. // Only one pin, and it's not a no-connect pin
  1995. if( pin && !has_other_connections
  1996. && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
  1997. && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  1998. {
  1999. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  2000. ercItem->SetItems( pin );
  2001. SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
  2002. screen->Append( marker );
  2003. ok = false;
  2004. }
  2005. // If there are multiple pins in this SG, they might be indirectly connected (by netname)
  2006. // rather than directly connected (by wires). We want to flag dangling pins even if they
  2007. // join nets with another pin, as it's often a mistake
  2008. if( pins.size() > 1 )
  2009. {
  2010. for( SCH_PIN* testPin : pins )
  2011. {
  2012. // We only apply this test to power symbols, because other symbols have invisible
  2013. // pins that are meant to be dangling, but the KiCad standard library power symbols
  2014. // have invisible pins that are *not* meant to be dangling.
  2015. if( testPin->GetLibPin()->GetParent()->IsPower()
  2016. && testPin->ConnectedItems( sheet ).empty()
  2017. && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
  2018. {
  2019. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
  2020. ercItem->SetItems( testPin );
  2021. SCH_MARKER* marker = new SCH_MARKER( ercItem,
  2022. testPin->GetTransformedPosition() );
  2023. screen->Append( marker );
  2024. ok = false;
  2025. }
  2026. }
  2027. }
  2028. }
  2029. return ok;
  2030. }
  2031. bool CONNECTION_GRAPH::ercCheckFloatingWires( const CONNECTION_SUBGRAPH* aSubgraph )
  2032. {
  2033. if( aSubgraph->m_driver )
  2034. return true;
  2035. std::vector<SCH_LINE*> wires;
  2036. // We've gotten this far, so we know we have no valid driver. All we need to do is check
  2037. // for a wire that we can place the error on.
  2038. for( SCH_ITEM* item : aSubgraph->m_items )
  2039. {
  2040. if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
  2041. wires.emplace_back( static_cast<SCH_LINE*>( item ) );
  2042. }
  2043. if( !wires.empty() )
  2044. {
  2045. SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
  2046. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
  2047. ercItem->SetItems( wires[0],
  2048. wires.size() > 1 ? wires[1] : nullptr,
  2049. wires.size() > 2 ? wires[2] : nullptr,
  2050. wires.size() > 3 ? wires[3] : nullptr );
  2051. SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
  2052. screen->Append( marker );
  2053. return false;
  2054. }
  2055. return true;
  2056. }
  2057. bool CONNECTION_GRAPH::ercCheckLabels( const CONNECTION_SUBGRAPH* aSubgraph )
  2058. {
  2059. // Label connection rules:
  2060. // Local labels are flagged if they don't connect to any pins and don't have a no-connect
  2061. // Global labels are flagged if they appear only once, don't connect to any local labels,
  2062. // and don't have a no-connect marker
  2063. // So, if there is a no-connect, we will never generate a warning here
  2064. if( aSubgraph->m_no_connect )
  2065. return true;
  2066. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  2067. bool ok = true;
  2068. SCH_TEXT* text = nullptr;
  2069. bool hasOtherConnections = false;
  2070. int pinCount = 0;
  2071. for( auto item : aSubgraph->m_items )
  2072. {
  2073. switch( item->Type() )
  2074. {
  2075. case SCH_LABEL_T:
  2076. case SCH_GLOBAL_LABEL_T:
  2077. case SCH_HIER_LABEL_T:
  2078. {
  2079. text = static_cast<SCH_TEXT*>( item );
  2080. // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
  2081. // we want to error if an individual label in the subgraph is floating, even if it's
  2082. // connected to other valid things by way of another label on the same sheet.
  2083. if( text->IsDangling() && settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
  2084. {
  2085. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
  2086. ercItem->SetItems( text );
  2087. SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
  2088. aSubgraph->m_sheet.LastScreen()->Append( marker );
  2089. ok = false;
  2090. }
  2091. break;
  2092. }
  2093. case SCH_PIN_T:
  2094. case SCH_SHEET_PIN_T:
  2095. hasOtherConnections = true;
  2096. pinCount++;
  2097. break;
  2098. default:
  2099. break;
  2100. }
  2101. }
  2102. if( !text )
  2103. return true;
  2104. bool isGlobal = text->Type() == SCH_GLOBAL_LABEL_T;
  2105. int errCode = isGlobal ? ERCE_GLOBLABEL : ERCE_LABEL_NOT_CONNECTED;
  2106. wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
  2107. wxString name = EscapeString( text->GetShownText(), CTX_NETNAME );
  2108. if( isGlobal )
  2109. {
  2110. // This will be set to true if the global is connected to a pin above, but we
  2111. // want to reset this to false so that globals get flagged if they only have a
  2112. // single instance connected to a single pin
  2113. hasOtherConnections = ( pinCount < 2 );
  2114. auto it = m_net_name_to_subgraphs_map.find( name );
  2115. if( it != m_net_name_to_subgraphs_map.end() )
  2116. {
  2117. if( it->second.size() > 1 || aSubgraph->m_multiple_drivers )
  2118. hasOtherConnections = true;
  2119. }
  2120. }
  2121. else if( text->Type() == SCH_HIER_LABEL_T )
  2122. {
  2123. // For a hier label, check if the parent pin is connected
  2124. if( aSubgraph->m_hier_parent &&
  2125. ( aSubgraph->m_hier_parent->m_strong_driver ||
  2126. aSubgraph->m_hier_parent->m_drivers.size() > 1) )
  2127. {
  2128. // For now, a simple check: if there is more than one driver, the parent is probably
  2129. // connected elsewhere (because at least one driver will be the hier pin itself)
  2130. hasOtherConnections = true;
  2131. }
  2132. }
  2133. else
  2134. {
  2135. auto pair = std::make_pair( aSubgraph->m_sheet, name );
  2136. auto it = m_local_label_cache.find( pair );
  2137. if( it != m_local_label_cache.end() && it->second.size() > 1 )
  2138. hasOtherConnections = true;
  2139. }
  2140. if( !hasOtherConnections && settings.IsTestEnabled( errCode ) )
  2141. {
  2142. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
  2143. ercItem->SetItems( text );
  2144. SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
  2145. aSubgraph->m_sheet.LastScreen()->Append( marker );
  2146. return false;
  2147. }
  2148. return ok;
  2149. }
  2150. int CONNECTION_GRAPH::ercCheckHierSheets()
  2151. {
  2152. int errors = 0;
  2153. for( const SCH_SHEET_PATH& sheet : m_sheetList )
  2154. {
  2155. for( SCH_ITEM* item : sheet.LastScreen()->Items() )
  2156. {
  2157. if( item->Type() != SCH_SHEET_T )
  2158. continue;
  2159. SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
  2160. std::map<wxString, SCH_SHEET_PIN*> pins;
  2161. for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
  2162. pins[pin->GetText()] = pin;
  2163. for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
  2164. {
  2165. if( subItem->Type() == SCH_HIER_LABEL_T )
  2166. {
  2167. SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
  2168. pins.erase( label->GetText() );
  2169. }
  2170. }
  2171. for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
  2172. {
  2173. wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
  2174. "label inside the sheet" ),
  2175. UnescapeString( unmatched.first ) );
  2176. std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
  2177. ercItem->SetItems( unmatched.second );
  2178. ercItem->SetErrorMessage( msg );
  2179. SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
  2180. sheet.LastScreen()->Append( marker );
  2181. errors++;
  2182. }
  2183. }
  2184. }
  2185. return errors;
  2186. }