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.

2330 lines
75 KiB

  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 <advanced_config.h>
  28. #include <common.h>
  29. #include <erc.h>
  30. #include <sch_edit_frame.h>
  31. #include <sch_bus_entry.h>
  32. #include <sch_component.h>
  33. #include <sch_line.h>
  34. #include <sch_pin.h>
  35. #include <sch_screen.h>
  36. #include <sch_sheet.h>
  37. #include <sch_sheet_path.h>
  38. #include <sch_text.h>
  39. #include <connection_graph.h>
  40. bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCreateMarkers )
  41. {
  42. int highest_priority = -1;
  43. std::vector<SCH_ITEM*> candidates;
  44. std::vector<SCH_ITEM*> strong_drivers;
  45. m_driver = nullptr;
  46. // Hierarchical labels are lower priority than local labels here,
  47. // because on the first pass we want local labels to drive subgraphs
  48. // so that we can identify same-sheet neighbors and link them together.
  49. // Hierarchical labels will end up overriding the final net name if
  50. // a higher-level sheet has a different name during the hierarchical
  51. // pass.
  52. for( auto item : m_drivers )
  53. {
  54. PRIORITY item_priority = GetDriverPriority( item );
  55. if( item_priority == PRIORITY_PIN &&
  56. !static_cast<SCH_PIN*>( item )->GetParentComponent()->IsInNetlist() )
  57. continue;
  58. if( item_priority >= PRIORITY_HIER_LABEL )
  59. strong_drivers.push_back( item );
  60. if( item_priority > highest_priority )
  61. {
  62. candidates.clear();
  63. candidates.push_back( item );
  64. highest_priority = item_priority;
  65. }
  66. else if( !candidates.empty() && ( item_priority == highest_priority ) )
  67. {
  68. candidates.push_back( item );
  69. }
  70. }
  71. if( highest_priority >= PRIORITY_HIER_LABEL )
  72. m_strong_driver = true;
  73. // Power pins are 5, global labels are 6
  74. m_local_driver = ( highest_priority < PRIORITY_POWER_PIN );
  75. if( !candidates.empty() )
  76. {
  77. if( candidates.size() > 1 )
  78. {
  79. if( highest_priority == PRIORITY_SHEET_PIN )
  80. {
  81. // We have multiple options, and they are all hierarchical
  82. // sheet pins. Let's prefer outputs over inputs.
  83. for( auto c : candidates )
  84. {
  85. auto p = static_cast<SCH_SHEET_PIN*>( c );
  86. if( p->GetShape() == NET_OUTPUT )
  87. {
  88. m_driver = c;
  89. break;
  90. }
  91. }
  92. }
  93. else
  94. {
  95. // For all other driver types, sort by name
  96. std::sort( candidates.begin(), candidates.end(),
  97. [&] ( SCH_ITEM* a, SCH_ITEM* b) -> bool
  98. {
  99. return GetNameForDriver( a ) < GetNameForDriver( b );
  100. } );
  101. }
  102. }
  103. if( !m_driver )
  104. m_driver = candidates[0];
  105. }
  106. if( strong_drivers.size() > 1 )
  107. m_multiple_drivers = true;
  108. // Drop weak drivers
  109. if( m_strong_driver )
  110. m_drivers = strong_drivers;
  111. // Cache driver connection
  112. if( m_driver )
  113. m_driver_connection = m_driver->Connection( m_sheet );
  114. else
  115. m_driver_connection = nullptr;
  116. if( aCreateMarkers && m_multiple_drivers )
  117. {
  118. // First check if all the candidates are actually the same
  119. bool same = true;
  120. auto first = GetNameForDriver( candidates[0] );
  121. for( unsigned i = 1; i < candidates.size(); i++ )
  122. {
  123. if( GetNameForDriver( candidates[i] ) != first )
  124. {
  125. same = false;
  126. break;
  127. }
  128. }
  129. if( !same )
  130. {
  131. wxString msg;
  132. msg.Printf( _( "%s and %s are both attached to the same wires. "
  133. "%s was picked as the label to use for netlisting." ),
  134. candidates[0]->GetSelectMenuText( m_frame->GetUserUnits() ),
  135. candidates[1]->GetSelectMenuText( m_frame->GetUserUnits() ),
  136. candidates[0]->Connection( m_sheet )->Name() );
  137. wxASSERT( candidates[0] != candidates[1] );
  138. auto p0 = ( candidates[0]->Type() == SCH_PIN_T ) ?
  139. static_cast<SCH_PIN*>( candidates[0] )->GetTransformedPosition() :
  140. candidates[0]->GetPosition();
  141. auto p1 = ( candidates[1]->Type() == SCH_PIN_T ) ?
  142. static_cast<SCH_PIN*>( candidates[1] )->GetTransformedPosition() :
  143. candidates[1]->GetPosition();
  144. auto marker = new SCH_MARKER();
  145. marker->SetTimeStamp( GetNewTimeStamp() );
  146. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  147. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  148. marker->SetData( ERCE_DRIVER_CONFLICT, p0, msg, p1 );
  149. m_sheet.LastScreen()->Append( marker );
  150. // If aCreateMarkers is true, then this is part of ERC check, so we
  151. // should return false even if the driver was assigned
  152. return false;
  153. }
  154. }
  155. return aCreateMarkers || ( m_driver != nullptr );
  156. }
  157. wxString CONNECTION_SUBGRAPH::GetNetName() const
  158. {
  159. if( !m_driver || m_dirty )
  160. return "";
  161. if( !m_driver->Connection( m_sheet ) )
  162. {
  163. #ifdef CONNECTIVITY_DEBUG
  164. wxASSERT_MSG( false, "Tried to get the net name of an item with no connection" );
  165. #endif
  166. return "";
  167. }
  168. return m_driver->Connection( m_sheet )->Name();
  169. }
  170. std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetBusLabels() const
  171. {
  172. std::vector<SCH_ITEM*> labels;
  173. for( auto item : m_drivers )
  174. {
  175. switch( item->Type() )
  176. {
  177. case SCH_LABEL_T:
  178. case SCH_GLOBAL_LABEL_T:
  179. {
  180. auto label_conn = item->Connection( m_sheet );
  181. // Only consider bus vectors
  182. if( label_conn->Type() == CONNECTION_BUS )
  183. labels.push_back( item );
  184. }
  185. default: break;
  186. }
  187. }
  188. return labels;
  189. }
  190. wxString CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem ) const
  191. {
  192. wxString name;
  193. switch( aItem->Type() )
  194. {
  195. case SCH_PIN_T:
  196. {
  197. auto power_object = static_cast<SCH_PIN*>( aItem );
  198. name = power_object->GetDefaultNetName( m_sheet );
  199. break;
  200. }
  201. case SCH_LABEL_T:
  202. case SCH_GLOBAL_LABEL_T:
  203. case SCH_HIER_LABEL_T:
  204. case SCH_SHEET_PIN_T:
  205. {
  206. name = static_cast<SCH_TEXT*>( aItem )->GetText();
  207. break;
  208. }
  209. default:
  210. break;
  211. }
  212. return name;
  213. }
  214. void CONNECTION_SUBGRAPH::Absorb( CONNECTION_SUBGRAPH* aOther )
  215. {
  216. wxASSERT( m_sheet == aOther->m_sheet );
  217. for( SCH_ITEM* item : aOther->m_items )
  218. {
  219. item->Connection( m_sheet )->SetSubgraphCode( m_code );
  220. AddItem( item );
  221. }
  222. m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
  223. m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
  224. aOther->m_absorbed = true;
  225. aOther->m_dirty = false;
  226. aOther->m_driver = nullptr;
  227. aOther->m_driver_connection = nullptr;
  228. aOther->m_absorbed_by = this;
  229. }
  230. void CONNECTION_SUBGRAPH::AddItem( SCH_ITEM* aItem )
  231. {
  232. m_items.push_back( aItem );
  233. if( aItem->Connection( m_sheet )->IsDriver() )
  234. m_drivers.push_back( aItem );
  235. if( aItem->Type() == SCH_SHEET_PIN_T )
  236. m_hier_pins.push_back( static_cast<SCH_SHEET_PIN*>( aItem ) );
  237. else if( aItem->Type() == SCH_HIER_LABEL_T )
  238. m_hier_ports.push_back( static_cast<SCH_HIERLABEL*>( aItem ) );
  239. }
  240. void CONNECTION_SUBGRAPH::UpdateItemConnections()
  241. {
  242. if( !m_driver_connection )
  243. return;
  244. for( auto item : m_items )
  245. {
  246. auto item_conn = item->Connection( m_sheet );
  247. if( !item_conn )
  248. item_conn = item->InitializeConnection( m_sheet );
  249. if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
  250. ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
  251. {
  252. continue;
  253. }
  254. if( item != m_driver )
  255. {
  256. item_conn->Clone( *m_driver_connection );
  257. item_conn->ClearDirty();
  258. }
  259. }
  260. }
  261. CONNECTION_SUBGRAPH::PRIORITY CONNECTION_SUBGRAPH::GetDriverPriority( SCH_ITEM* aDriver )
  262. {
  263. if( !aDriver )
  264. return PRIORITY_NONE;
  265. switch( aDriver->Type() )
  266. {
  267. case SCH_SHEET_PIN_T: return PRIORITY_SHEET_PIN;
  268. case SCH_HIER_LABEL_T: return PRIORITY_HIER_LABEL;
  269. case SCH_LABEL_T: return PRIORITY_LOCAL_LABEL;
  270. case SCH_GLOBAL_LABEL_T: return PRIORITY_GLOBAL;
  271. case SCH_PIN_T:
  272. {
  273. auto sch_pin = static_cast<SCH_PIN*>( aDriver );
  274. if( sch_pin->IsPowerConnection() )
  275. return PRIORITY_POWER_PIN;
  276. else
  277. return PRIORITY_PIN;
  278. }
  279. default: return PRIORITY_NONE;
  280. }
  281. }
  282. bool CONNECTION_GRAPH::m_allowRealTime = true;
  283. void CONNECTION_GRAPH::Reset()
  284. {
  285. for( auto subgraph : m_subgraphs )
  286. delete subgraph;
  287. m_items.clear();
  288. m_subgraphs.clear();
  289. m_driver_subgraphs.clear();
  290. m_sheet_to_subgraphs_map.clear();
  291. m_invisible_power_pins.clear();
  292. m_bus_alias_cache.clear();
  293. m_net_name_to_code_map.clear();
  294. m_bus_name_to_code_map.clear();
  295. m_net_code_to_subgraphs_map.clear();
  296. m_net_name_to_subgraphs_map.clear();
  297. m_local_label_cache.clear();
  298. m_global_label_cache.clear();
  299. m_last_net_code = 1;
  300. m_last_bus_code = 1;
  301. m_last_subgraph_code = 1;
  302. }
  303. void CONNECTION_GRAPH::Recalculate( SCH_SHEET_LIST aSheetList, bool aUnconditional )
  304. {
  305. PROF_COUNTER recalc_time;
  306. PROF_COUNTER update_items;
  307. if( aUnconditional )
  308. Reset();
  309. for( const auto& sheet : aSheetList )
  310. {
  311. std::vector<SCH_ITEM*> items;
  312. for( auto item = sheet.LastScreen()->GetDrawItems();
  313. item; item = item->Next() )
  314. {
  315. if( item->IsConnectable() &&
  316. ( aUnconditional || item->IsConnectivityDirty() ) )
  317. {
  318. items.push_back( item );
  319. }
  320. }
  321. updateItemConnectivity( sheet, items );
  322. }
  323. update_items.Stop();
  324. wxLogTrace( "CONN_PROFILE", "UpdateItemConnectivity() %0.4f ms", update_items.msecs() );
  325. PROF_COUNTER tde;
  326. // IsDanglingStateChanged() also adds connected items for things like SCH_TEXT
  327. SCH_SCREENS schematic;
  328. schematic.TestDanglingEnds();
  329. tde.Stop();
  330. wxLogTrace( "CONN_PROFILE", "TestDanglingEnds() %0.4f ms", tde.msecs() );
  331. PROF_COUNTER build_graph;
  332. buildConnectionGraph();
  333. build_graph.Stop();
  334. wxLogTrace( "CONN_PROFILE", "BuildConnectionGraph() %0.4f ms", build_graph.msecs() );
  335. recalc_time.Stop();
  336. wxLogTrace( "CONN_PROFILE", "Recalculate time %0.4f ms", recalc_time.msecs() );
  337. #ifndef DEBUG
  338. // Pressure relief valve for release builds
  339. const double max_recalc_time_msecs = 250.;
  340. if( m_allowRealTime && ADVANCED_CFG::GetCfg().m_realTimeConnectivity &&
  341. recalc_time.msecs() > max_recalc_time_msecs )
  342. {
  343. m_allowRealTime = false;
  344. }
  345. #endif
  346. }
  347. void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet,
  348. std::vector<SCH_ITEM*> aItemList )
  349. {
  350. std::unordered_map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
  351. for( auto item : aItemList )
  352. {
  353. std::vector< wxPoint > points;
  354. item->GetConnectionPoints( points );
  355. item->ConnectedItems().clear();
  356. if( item->Type() == SCH_SHEET_T )
  357. {
  358. for( auto& pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  359. {
  360. if( !pin.Connection( aSheet ) )
  361. {
  362. pin.InitializeConnection( aSheet );
  363. }
  364. pin.ConnectedItems().clear();
  365. pin.Connection( aSheet )->Reset();
  366. connection_map[ pin.GetTextPos() ].push_back( &pin );
  367. m_items.insert( &pin );
  368. }
  369. }
  370. else if( item->Type() == SCH_COMPONENT_T )
  371. {
  372. SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
  373. TRANSFORM t = component->GetTransform();
  374. // Assumption: we don't need to call UpdatePins() here because anything
  375. // that would change the pins of the component will have called it already
  376. for( SCH_PIN& pin : component->GetPins() )
  377. {
  378. pin.InitializeConnection( aSheet );
  379. wxPoint pos = t.TransformCoordinate( pin.GetPosition() ) + component->GetPosition();
  380. // because calling the first time is not thread-safe
  381. pin.GetDefaultNetName( aSheet );
  382. pin.ConnectedItems().clear();
  383. // Invisible power pins need to be post-processed later
  384. if( pin.IsPowerConnection() && !pin.IsVisible() )
  385. m_invisible_power_pins.emplace_back( std::make_pair( aSheet, &pin ) );
  386. connection_map[ pos ].push_back( &pin );
  387. m_items.insert( &pin );
  388. }
  389. }
  390. else
  391. {
  392. m_items.insert( item );
  393. auto conn = item->InitializeConnection( aSheet );
  394. // Set bus/net property here so that the propagation code uses it
  395. switch( item->Type() )
  396. {
  397. case SCH_LINE_T:
  398. conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_BUS : CONNECTION_NET );
  399. break;
  400. case SCH_BUS_BUS_ENTRY_T:
  401. conn->SetType( CONNECTION_BUS );
  402. break;
  403. case SCH_PIN_T:
  404. case SCH_BUS_WIRE_ENTRY_T:
  405. conn->SetType( CONNECTION_NET );
  406. break;
  407. default:
  408. break;
  409. }
  410. for( auto point : points )
  411. {
  412. connection_map[ point ].push_back( item );
  413. }
  414. }
  415. item->SetConnectivityDirty( false );
  416. }
  417. for( const auto& it : connection_map )
  418. {
  419. auto connection_vec = it.second;
  420. SCH_ITEM* junction = nullptr;
  421. for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
  422. {
  423. auto connected_item = *primary_it;
  424. // Look for junctions. For points that have a junction, we want all
  425. // items to connect to the junction but not to each other.
  426. if( connected_item->Type() == SCH_JUNCTION_T )
  427. {
  428. junction = connected_item;
  429. }
  430. // Bus entries are special: they can have connection points in the
  431. // middle of a wire segment, because the junction algo doesn't split
  432. // the segment in two where you place a bus entry. This means that
  433. // bus entries that don't land on the end of a line segment need to
  434. // have "virtual" connection points to the segments they graphically
  435. // touch.
  436. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  437. {
  438. // If this location only has the connection point of the bus
  439. // entry itself, this means that either the bus entry is not
  440. // connected to anything graphically, or that it is connected to
  441. // a segment at some point other than at one of the endpoints.
  442. if( connection_vec.size() == 1 )
  443. {
  444. auto screen = aSheet.LastScreen();
  445. auto bus = screen->GetBus( it.first );
  446. if( bus )
  447. {
  448. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  449. bus_entry->m_connected_bus_item = bus;
  450. }
  451. }
  452. }
  453. // Bus-to-bus entries are treated just like bus wires
  454. if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
  455. {
  456. if( connection_vec.size() < 2 )
  457. {
  458. auto screen = aSheet.LastScreen();
  459. auto bus = screen->GetBus( it.first );
  460. if( bus )
  461. {
  462. auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
  463. if( it.first == bus_entry->GetPosition() )
  464. bus_entry->m_connected_bus_items[0] = bus;
  465. else
  466. bus_entry->m_connected_bus_items[1] = bus;
  467. bus_entry->ConnectedItems().insert( bus );
  468. bus->ConnectedItems().insert( bus_entry );
  469. }
  470. }
  471. }
  472. for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
  473. {
  474. auto test_item = *test_it;
  475. if( !junction && test_item->Type() == SCH_JUNCTION_T )
  476. {
  477. junction = test_item;
  478. }
  479. if( connected_item != test_item &&
  480. connected_item != junction &&
  481. connected_item->ConnectionPropagatesTo( test_item ) &&
  482. test_item->ConnectionPropagatesTo( connected_item ) )
  483. {
  484. connected_item->ConnectedItems().insert( test_item );
  485. test_item->ConnectedItems().insert( connected_item );
  486. }
  487. // Set up the link between the bus entry net and the bus
  488. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  489. {
  490. if( test_item->Connection( aSheet )->IsBus() )
  491. {
  492. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  493. bus_entry->m_connected_bus_item = test_item;
  494. }
  495. }
  496. }
  497. // If we got this far and did not find a connected bus item for a bus entry,
  498. // we should do a manual scan in case there is a bus item on this connection
  499. // point but we didn't pick it up earlier because there is *also* a net item here.
  500. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  501. {
  502. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  503. if( !bus_entry->m_connected_bus_item )
  504. {
  505. auto screen = aSheet.LastScreen();
  506. auto bus = screen->GetBus( it.first );
  507. if( bus )
  508. bus_entry->m_connected_bus_item = bus;
  509. }
  510. }
  511. }
  512. }
  513. }
  514. // TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
  515. // to the same subgraph necessarily if it runs over and over again on the same
  516. // sheet. We need:
  517. //
  518. // a) a cache of net/bus codes, like used before
  519. // b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
  520. // c) some way of trying to avoid changing net names. so we should keep track
  521. // of the previous driver of a net, and if it comes down to choosing between
  522. // equally-prioritized drivers, choose the one that already exists as a driver
  523. // on some portion of the items.
  524. void CONNECTION_GRAPH::buildConnectionGraph()
  525. {
  526. // Recache all bus aliases for later use
  527. SCH_SHEET_LIST all_sheets( g_RootSheet );
  528. for( unsigned i = 0; i < all_sheets.size(); i++ )
  529. {
  530. for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() )
  531. {
  532. m_bus_alias_cache[ alias->GetName() ] = alias;
  533. }
  534. }
  535. // Build subgraphs from items (on a per-sheet basis)
  536. for( SCH_ITEM* item : m_items )
  537. {
  538. for( const auto& it : item->m_connection_map )
  539. {
  540. const auto sheet = it.first;
  541. auto connection = it.second;
  542. if( connection->SubgraphCode() == 0 )
  543. {
  544. auto subgraph = new CONNECTION_SUBGRAPH( m_frame );
  545. subgraph->m_code = m_last_subgraph_code++;
  546. subgraph->m_sheet = sheet;
  547. subgraph->AddItem( item );
  548. connection->SetSubgraphCode( subgraph->m_code );
  549. std::list<SCH_ITEM*> members;
  550. auto get_items = [ &sheet ] ( SCH_ITEM* aItem ) -> bool
  551. {
  552. auto* conn = aItem->Connection( sheet );
  553. if( !conn )
  554. conn = aItem->InitializeConnection( sheet );
  555. return ( conn->SubgraphCode() == 0 );
  556. };
  557. std::copy_if( item->ConnectedItems().begin(),
  558. item->ConnectedItems().end(),
  559. std::back_inserter( members ), get_items );
  560. for( auto connected_item : members )
  561. {
  562. if( connected_item->Type() == SCH_NO_CONNECT_T )
  563. subgraph->m_no_connect = connected_item;
  564. auto connected_conn = connected_item->Connection( sheet );
  565. wxASSERT( connected_conn );
  566. if( connected_conn->SubgraphCode() == 0 )
  567. {
  568. connected_conn->SetSubgraphCode( subgraph->m_code );
  569. subgraph->AddItem( connected_item );
  570. std::copy_if( connected_item->ConnectedItems().begin(),
  571. connected_item->ConnectedItems().end(),
  572. std::back_inserter( members ), get_items );
  573. }
  574. }
  575. subgraph->m_dirty = true;
  576. m_subgraphs.push_back( subgraph );
  577. }
  578. }
  579. }
  580. /**
  581. * TODO(JE)
  582. *
  583. * It would be good if net codes were preserved as much as possible when
  584. * generating netlists, so that unnamed nets don't keep shifting around when
  585. * you regenerate.
  586. *
  587. * Right now, we are clearing out the old connections up in
  588. * UpdateItemConnectivity(), but that is useful information, so maybe we
  589. * need to just set the dirty flag or something.
  590. *
  591. * That way, ResolveDrivers() can check what the driver of the subgraph was
  592. * previously, and if it is in the situation of choosing between equal
  593. * candidates for an auto-generated net name, pick the previous one.
  594. *
  595. * N.B. the old algorithm solves this by sorting the possible net names
  596. * alphabetically, so as long as the same refdes components are involved,
  597. * the net will be the same.
  598. */
  599. // Resolve drivers for subgraphs and propagate connectivity info
  600. // We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
  601. size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
  602. ( m_subgraphs.size() + 3 ) / 4 );
  603. std::atomic<size_t> nextSubgraph( 0 );
  604. std::vector<std::future<size_t>> returns( parallelThreadCount );
  605. std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
  606. std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
  607. [&] ( const CONNECTION_SUBGRAPH* candidate ) {
  608. return candidate->m_dirty;
  609. } );
  610. auto update_lambda = [&nextSubgraph, &dirty_graphs]() -> size_t
  611. {
  612. for( size_t subgraphId = nextSubgraph++; subgraphId < dirty_graphs.size(); subgraphId = nextSubgraph++ )
  613. {
  614. auto subgraph = dirty_graphs[subgraphId];
  615. if( !subgraph->m_dirty )
  616. continue;
  617. // Special processing for some items
  618. for( auto item : subgraph->m_items )
  619. {
  620. switch( item->Type() )
  621. {
  622. case SCH_NO_CONNECT_T:
  623. subgraph->m_no_connect = item;
  624. break;
  625. case SCH_BUS_WIRE_ENTRY_T:
  626. subgraph->m_bus_entry = item;
  627. break;
  628. case SCH_PIN_T:
  629. {
  630. auto pin = static_cast<SCH_PIN*>( item );
  631. if( pin->GetType() == PIN_NC )
  632. subgraph->m_no_connect = item;
  633. break;
  634. }
  635. default:
  636. break;
  637. }
  638. }
  639. if( !subgraph->ResolveDrivers() )
  640. {
  641. subgraph->m_dirty = false;
  642. }
  643. else
  644. {
  645. // Now the subgraph has only one driver
  646. SCH_ITEM* driver = subgraph->m_driver;
  647. SCH_SHEET_PATH sheet = subgraph->m_sheet;
  648. SCH_CONNECTION* connection = driver->Connection( sheet );
  649. // TODO(JE) This should live in SCH_CONNECTION probably
  650. switch( driver->Type() )
  651. {
  652. case SCH_LABEL_T:
  653. case SCH_GLOBAL_LABEL_T:
  654. case SCH_HIER_LABEL_T:
  655. {
  656. auto text = static_cast<SCH_TEXT*>( driver );
  657. connection->ConfigureFromLabel( text->GetShownText() );
  658. break;
  659. }
  660. case SCH_SHEET_PIN_T:
  661. {
  662. auto pin = static_cast<SCH_SHEET_PIN*>( driver );
  663. connection->ConfigureFromLabel( pin->GetShownText() );
  664. break;
  665. }
  666. case SCH_PIN_T:
  667. {
  668. auto pin = static_cast<SCH_PIN*>( driver );
  669. // NOTE(JE) GetDefaultNetName is not thread-safe.
  670. connection->ConfigureFromLabel( pin->GetDefaultNetName( sheet ) );
  671. break;
  672. }
  673. default:
  674. wxLogTrace( "CONN", "Driver type unsupported: %s",
  675. driver->GetSelectMenuText( MILLIMETRES ) );
  676. break;
  677. }
  678. connection->SetDriver( driver );
  679. connection->ClearDirty();
  680. subgraph->m_dirty = false;
  681. }
  682. }
  683. return 1;
  684. };
  685. if( parallelThreadCount == 1 )
  686. update_lambda();
  687. else
  688. {
  689. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  690. returns[ii] = std::async( std::launch::async, update_lambda );
  691. // Finalize the threads
  692. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  693. returns[ii].wait();
  694. }
  695. // Now discard any non-driven subgraphs from further consideration
  696. std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
  697. [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool {
  698. return candidate->m_driver;
  699. } );
  700. // Check for subgraphs with the same net name but only weak drivers.
  701. // For example, two wires that are both connected to hierarchical
  702. // sheet pins that happen to have the same name, but are not the same.
  703. for( auto&& subgraph : m_driver_subgraphs )
  704. {
  705. wxString full_name = subgraph->m_driver_connection->Name();
  706. wxString name = subgraph->m_driver_connection->Name( true );
  707. m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
  708. subgraph->m_dirty = true;
  709. if( subgraph->m_strong_driver )
  710. {
  711. SCH_ITEM* driver = subgraph->m_driver;
  712. SCH_SHEET_PATH sheet = subgraph->m_sheet;
  713. switch( driver->Type() )
  714. {
  715. case SCH_LABEL_T:
  716. case SCH_HIER_LABEL_T:
  717. {
  718. m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
  719. break;
  720. }
  721. case SCH_GLOBAL_LABEL_T:
  722. {
  723. m_global_label_cache[name].push_back( subgraph );
  724. break;
  725. }
  726. case SCH_PIN_T:
  727. {
  728. auto pin = static_cast<SCH_PIN*>( driver );
  729. wxASSERT( pin->IsPowerConnection() );
  730. m_global_label_cache[name].push_back( subgraph );
  731. break;
  732. }
  733. default:
  734. wxLogTrace( "CONN", "Unexpected strong driver %s",
  735. driver->GetSelectMenuText( MILLIMETRES ) );
  736. break;
  737. }
  738. }
  739. }
  740. // Generate subgraphs for invisible power pins. These will be merged with other subgraphs
  741. // on the same sheet in the next loop.
  742. std::unordered_map<int, CONNECTION_SUBGRAPH*> invisible_pin_subgraphs;
  743. for( const auto& it : m_invisible_power_pins )
  744. {
  745. SCH_PIN* pin = it.second;
  746. if( !pin->ConnectedItems().empty() && !pin->GetLibPin()->GetParent()->IsPower() )
  747. {
  748. // ERC will warn about this: user has wired up an invisible pin
  749. continue;
  750. }
  751. SCH_SHEET_PATH sheet = it.first;
  752. SCH_CONNECTION* connection = pin->Connection( sheet );
  753. if( !connection )
  754. connection = pin->InitializeConnection( sheet );
  755. // If this pin already has a subgraph, don't need to process
  756. if( connection->SubgraphCode() > 0 )
  757. continue;
  758. connection->SetName( pin->GetName() );
  759. int code = assignNewNetCode( *connection );
  760. connection->SetNetCode( code );
  761. CONNECTION_SUBGRAPH* subgraph;
  762. if( invisible_pin_subgraphs.count( code ) )
  763. {
  764. subgraph = invisible_pin_subgraphs.at( code );
  765. subgraph->AddItem( pin );
  766. }
  767. else
  768. {
  769. subgraph = new CONNECTION_SUBGRAPH( m_frame );
  770. subgraph->m_code = m_last_subgraph_code++;
  771. subgraph->m_sheet = sheet;
  772. subgraph->AddItem( pin );
  773. subgraph->ResolveDrivers();
  774. m_net_code_to_subgraphs_map[ code ].push_back( subgraph );
  775. m_subgraphs.push_back( subgraph );
  776. m_driver_subgraphs.push_back( subgraph );
  777. invisible_pin_subgraphs[code] = subgraph;
  778. }
  779. connection->SetSubgraphCode( subgraph->m_code );
  780. }
  781. for( auto it : invisible_pin_subgraphs )
  782. it.second->UpdateItemConnections();
  783. // Here we do all the local (sheet) processing of each subgraph, including assigning net
  784. // codes, merging subgraphs together that use label connections, etc.
  785. // Cache remaining valid subgraphs by sheet path
  786. for( auto subgraph : m_driver_subgraphs )
  787. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
  788. std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
  789. for( auto subgraph_it = m_driver_subgraphs.begin();
  790. subgraph_it != m_driver_subgraphs.end(); subgraph_it++ )
  791. {
  792. auto subgraph = *subgraph_it;
  793. if( subgraph->m_absorbed )
  794. continue;
  795. SCH_CONNECTION* connection = subgraph->m_driver_connection;
  796. SCH_SHEET_PATH sheet = subgraph->m_sheet;
  797. wxString name = connection->Name();
  798. // Test subgraphs with weak drivers for net name conflicts and fix them
  799. unsigned suffix = 1;
  800. auto create_new_name = [&] ( SCH_CONNECTION* aConn, wxString aName ) -> wxString {
  801. wxString new_name = wxString::Format( "%s_%u", aName, suffix );
  802. aConn->SetSuffix( wxString::Format( "_%u", suffix ) );
  803. suffix++;
  804. return new_name;
  805. };
  806. if( !subgraph->m_strong_driver )
  807. {
  808. auto& vec = m_net_name_to_subgraphs_map.at( name );
  809. if( vec.size() > 1 )
  810. {
  811. wxString new_name = create_new_name( connection, name );
  812. while( m_net_name_to_subgraphs_map.count( new_name ) )
  813. new_name = create_new_name( connection, name );
  814. wxLogTrace( "CONN", "%ld (%s) is weakly driven and not unique. Changing to %s.",
  815. subgraph->m_code, name, new_name );
  816. vec.erase( std::remove( vec.begin(), vec.end(), subgraph ), vec.end() );
  817. m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
  818. name = new_name;
  819. subgraph->UpdateItemConnections();
  820. }
  821. }
  822. // Assign net codes
  823. if( connection->IsBus() )
  824. {
  825. int code = -1;
  826. if( m_bus_name_to_code_map.count( name ) )
  827. {
  828. code = m_bus_name_to_code_map.at( name );
  829. }
  830. else
  831. {
  832. code = m_last_bus_code++;
  833. m_bus_name_to_code_map[ name ] = code;
  834. }
  835. connection->SetBusCode( code );
  836. assignNetCodesToBus( connection );
  837. }
  838. else
  839. {
  840. assignNewNetCode( *connection );
  841. }
  842. subgraph->UpdateItemConnections();
  843. // Reset the flag for the next loop below
  844. subgraph->m_dirty = true;
  845. // Next, we merge together subgraphs that have label connections, and create
  846. // neighbor links for subgraphs that are part of a bus on the same sheet.
  847. // For merging, we consider each possible strong driver.
  848. // If this subgraph doesn't have a strong driver, let's skip it, since there is no
  849. // way it will be merged with anything.
  850. if( !subgraph->m_strong_driver )
  851. continue;
  852. // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
  853. // as the subgraph we are considering that has a strong driver.
  854. // Weakly driven subgraphs are not considered since they will never be absorbed or
  855. // form neighbor links.
  856. std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
  857. std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
  858. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
  859. std::back_inserter( candidate_subgraphs ),
  860. [&] ( const CONNECTION_SUBGRAPH* candidate )
  861. { return ( !candidate->m_absorbed &&
  862. candidate->m_strong_driver &&
  863. candidate != subgraph );
  864. } );
  865. // This is a list of connections on the current subgraph to compare to the
  866. // drivers of each candidate subgraph. If the current subgraph is a bus,
  867. // we should consider each bus member.
  868. auto connections_to_check( connection->Members() );
  869. // Also check the main driving connection
  870. connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
  871. auto add_connections_to_check = [&] ( CONNECTION_SUBGRAPH* aSubgraph ) {
  872. for( SCH_ITEM* possible_driver : aSubgraph->m_items )
  873. {
  874. if( possible_driver == aSubgraph->m_driver )
  875. continue;
  876. switch( possible_driver->Type() )
  877. {
  878. case SCH_PIN_T:
  879. {
  880. auto sch_pin = static_cast<SCH_PIN*>( possible_driver );
  881. if( sch_pin->IsPowerConnection() )
  882. {
  883. auto c = std::make_shared<SCH_CONNECTION>( possible_driver,
  884. aSubgraph->m_sheet );
  885. c->SetName( static_cast<SCH_PIN *>( possible_driver )->GetName() );
  886. if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
  887. continue;
  888. connections_to_check.push_back( c );
  889. wxLogTrace( "CONN", "%lu (%s): Adding secondary pin %s",
  890. aSubgraph->m_code,
  891. aSubgraph->m_driver_connection->Name( true ),
  892. c->Name( true ) );
  893. }
  894. break;
  895. }
  896. case SCH_GLOBAL_LABEL_T:
  897. case SCH_HIER_LABEL_T:
  898. case SCH_LABEL_T:
  899. {
  900. auto c = std::make_shared<SCH_CONNECTION>( possible_driver,
  901. aSubgraph->m_sheet );
  902. c->SetName( static_cast<SCH_TEXT*>( possible_driver )->GetShownText() );
  903. if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
  904. continue;
  905. connections_to_check.push_back( c );
  906. wxLogTrace( "CONN", "%lu (%s): Adding secondary label %s",
  907. aSubgraph->m_code,
  908. aSubgraph->m_driver_connection->Name( true ),
  909. c->Name( true ) );
  910. break;
  911. }
  912. default:
  913. break;
  914. }
  915. }
  916. };
  917. // Now add other strong drivers
  918. // The actual connection attached to these items will have been overwritten
  919. // by the chosen driver of the subgraph, so we need to create a dummy connection
  920. add_connections_to_check( subgraph );
  921. for( unsigned i = 0; i < connections_to_check.size(); i++ )
  922. {
  923. auto member = connections_to_check[i];
  924. if( member->IsBus() )
  925. {
  926. connections_to_check.insert( connections_to_check.end(),
  927. member->Members().begin(),
  928. member->Members().end() );
  929. }
  930. wxString test_name = member->Name( true );
  931. for( auto candidate : candidate_subgraphs )
  932. {
  933. if( candidate->m_absorbed )
  934. continue;
  935. bool match = false;
  936. if( candidate->m_driver_connection->Name( true ) == test_name )
  937. {
  938. match = true;
  939. }
  940. else
  941. {
  942. if( !candidate->m_multiple_drivers )
  943. continue;
  944. for( SCH_ITEM *driver : candidate->m_drivers )
  945. {
  946. if( driver == candidate->m_driver )
  947. continue;
  948. // Sheet pins are not candidates for merging
  949. if( driver->Type() == SCH_SHEET_PIN_T )
  950. continue;
  951. if( driver->Type() == SCH_PIN_T )
  952. {
  953. if( static_cast<SCH_PIN*>( driver )->GetName() == test_name )
  954. {
  955. match = true;
  956. break;
  957. }
  958. }
  959. else
  960. {
  961. wxASSERT( driver->Type() == SCH_LABEL_T ||
  962. driver->Type() == SCH_GLOBAL_LABEL_T ||
  963. driver->Type() == SCH_HIER_LABEL_T );
  964. auto text = static_cast<SCH_TEXT*>( driver );
  965. if( text->GetShownText() == test_name )
  966. {
  967. match = true;
  968. break;
  969. }
  970. }
  971. }
  972. }
  973. if( match )
  974. {
  975. if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
  976. {
  977. wxLogTrace( "CONN", "%lu (%s) has neighbor %lu (%s)", subgraph->m_code,
  978. connection->Name(), candidate->m_code, member->Name() );
  979. subgraph->m_bus_neighbors[member].insert( candidate );
  980. candidate->m_bus_parents[member].insert( subgraph );
  981. }
  982. else
  983. {
  984. wxLogTrace( "CONN", "%lu (%s) absorbs neighbor %lu (%s)",
  985. subgraph->m_code, connection->Name(),
  986. candidate->m_code, candidate->m_driver_connection->Name() );
  987. subgraph->Absorb( candidate );
  988. invalidated_subgraphs.insert( subgraph );
  989. // Candidate may have other non-chosen drivers we need to follow
  990. add_connections_to_check( subgraph );
  991. }
  992. }
  993. }
  994. }
  995. }
  996. // Update any subgraph that was invalidated above
  997. for( auto subgraph : invalidated_subgraphs )
  998. {
  999. if( subgraph->m_absorbed )
  1000. continue;
  1001. subgraph->ResolveDrivers();
  1002. if( subgraph->m_driver_connection->IsBus() )
  1003. assignNetCodesToBus( subgraph->m_driver_connection );
  1004. else
  1005. assignNewNetCode( *subgraph->m_driver_connection );
  1006. subgraph->UpdateItemConnections();
  1007. wxLogTrace( "CONN", "Re-resolving drivers for %lu (%s)", subgraph->m_code,
  1008. subgraph->m_driver_connection->Name() );
  1009. }
  1010. // Absorbed subgraphs should no longer be considered
  1011. m_driver_subgraphs.erase( std::remove_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
  1012. [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool {
  1013. return candidate->m_absorbed;
  1014. } ), m_driver_subgraphs.end() );
  1015. // Store global subgraphs for later reference
  1016. std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
  1017. std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
  1018. std::back_inserter( global_subgraphs ),
  1019. [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool {
  1020. return !candidate->m_local_driver;
  1021. } );
  1022. // Recache remaining valid subgraphs by sheet path
  1023. m_sheet_to_subgraphs_map.clear();
  1024. for( auto subgraph : m_driver_subgraphs )
  1025. m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
  1026. // Next time through the subgraphs, we do some post-processing to handle things like
  1027. // connecting bus members to their neighboring subgraphs, and then propagate connections
  1028. // through the hierarchy
  1029. for( auto subgraph : m_driver_subgraphs )
  1030. {
  1031. if( !subgraph->m_dirty )
  1032. continue;
  1033. // For subgraphs that are driven by a global (power port or label) and have more
  1034. // than one global driver, we need to seek out other subgraphs driven by the
  1035. // same name as the non-chosen driver and update them to match the chosen one.
  1036. if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
  1037. {
  1038. for( SCH_ITEM* driver : subgraph->m_drivers )
  1039. {
  1040. if( driver == subgraph->m_driver )
  1041. continue;
  1042. wxString secondary_name = subgraph->GetNameForDriver( driver );
  1043. if( secondary_name == subgraph->m_driver_connection->Name() )
  1044. continue;
  1045. for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
  1046. {
  1047. if( candidate == subgraph )
  1048. continue;
  1049. SCH_CONNECTION* conn = candidate->m_driver_connection;
  1050. if( conn->Name() == secondary_name )
  1051. {
  1052. wxLogTrace( "CONN", "Global %lu (%s) promoted to %s", candidate->m_code,
  1053. conn->Name(), subgraph->m_driver_connection->Name() );
  1054. conn->Clone( *subgraph->m_driver_connection );
  1055. candidate->UpdateItemConnections();
  1056. candidate->m_dirty = false;
  1057. }
  1058. }
  1059. }
  1060. }
  1061. // This call will handle descending the hierarchy and updating child subgraphs
  1062. propagateToNeighbors( subgraph );
  1063. }
  1064. // Handle buses that have been linked together somewhere by member (net) connections.
  1065. // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
  1066. // For net subgraphs that have more than one bus parent, we need to ensure that those
  1067. // buses are linked together in the final netlist. The final name of each bus might not
  1068. // match the local name that was used to establish the parent-child relationship, because
  1069. // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
  1070. // we need to identify the appropriate bus members to link together (and their final names),
  1071. // and then update all instances of the old name in the hierarchy.
  1072. for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
  1073. {
  1074. if( subgraph->m_bus_parents.size() < 2 )
  1075. continue;
  1076. SCH_CONNECTION* conn = subgraph->m_driver_connection;
  1077. wxASSERT( conn->IsNet() );
  1078. for( const auto& it : subgraph->m_bus_parents )
  1079. {
  1080. SCH_CONNECTION* link_member = it.first.get();
  1081. for( CONNECTION_SUBGRAPH* parent : it.second )
  1082. {
  1083. SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
  1084. if( !match )
  1085. {
  1086. wxLogTrace( "CONN", "Warning: could not match %s inside %lu (%s)", conn->Name(),
  1087. parent->m_code, parent->m_driver_connection->Name() );
  1088. continue;
  1089. }
  1090. if( conn->Name() != match->Name() )
  1091. {
  1092. wxString old_name = match->Name();
  1093. wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", parent->m_code,
  1094. parent->m_driver_connection->Name(), old_name, conn->Name() );
  1095. match->Clone( *conn );
  1096. if( !m_net_name_to_subgraphs_map.count( old_name ) )
  1097. continue;
  1098. for( CONNECTION_SUBGRAPH* old_sg : m_net_name_to_subgraphs_map.at( old_name ) )
  1099. {
  1100. if( old_sg->m_absorbed )
  1101. old_sg = old_sg->m_absorbed_by;
  1102. old_sg->m_driver_connection->Clone( *conn );
  1103. old_sg->UpdateItemConnections();
  1104. }
  1105. }
  1106. }
  1107. }
  1108. }
  1109. m_net_code_to_subgraphs_map.clear();
  1110. for( auto subgraph : m_driver_subgraphs )
  1111. {
  1112. // Every driven subgraph should have been marked by now
  1113. if( subgraph->m_dirty )
  1114. {
  1115. // TODO(JE) this should be caught by hierarchical sheet port/pin ERC, check this
  1116. // Reset to false so no complaints come up later
  1117. subgraph->m_dirty = false;
  1118. }
  1119. if( subgraph->m_driver_connection->IsBus() )
  1120. continue;
  1121. int code = subgraph->m_driver_connection->NetCode();
  1122. m_net_code_to_subgraphs_map[ code ].push_back( subgraph );
  1123. }
  1124. m_subgraphs.erase( std::remove_if( m_subgraphs.begin(), m_subgraphs.end(),
  1125. [&] ( const CONNECTION_SUBGRAPH* sg ) {
  1126. return sg->m_absorbed;
  1127. } ), m_subgraphs.end() );
  1128. }
  1129. int CONNECTION_GRAPH::assignNewNetCode( SCH_CONNECTION& aConnection )
  1130. {
  1131. int code;
  1132. if( m_net_name_to_code_map.count( aConnection.Name() ) )
  1133. {
  1134. code = m_net_name_to_code_map.at( aConnection.Name() );
  1135. }
  1136. else
  1137. {
  1138. code = m_last_net_code++;
  1139. m_net_name_to_code_map[ aConnection.Name() ] = code;
  1140. }
  1141. aConnection.SetNetCode( code );
  1142. return code;
  1143. }
  1144. void CONNECTION_GRAPH::assignNetCodesToBus( SCH_CONNECTION* aConnection )
  1145. {
  1146. auto connections_to_check( aConnection->Members() );
  1147. for( unsigned i = 0; i < connections_to_check.size(); i++ )
  1148. {
  1149. auto member = connections_to_check[i];
  1150. if( member->IsBus() )
  1151. {
  1152. connections_to_check.insert( connections_to_check.end(),
  1153. member->Members().begin(),
  1154. member->Members().end() );
  1155. continue;
  1156. }
  1157. assignNewNetCode( *member );
  1158. }
  1159. }
  1160. void CONNECTION_GRAPH::propagateToNeighbors( CONNECTION_SUBGRAPH* aSubgraph )
  1161. {
  1162. SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
  1163. std::vector<CONNECTION_SUBGRAPH*> search_list;
  1164. std::unordered_set<CONNECTION_SUBGRAPH*> visited;
  1165. auto visit = [&] ( CONNECTION_SUBGRAPH* aParent ) {
  1166. for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
  1167. {
  1168. SCH_SHEET_PATH path = aParent->m_sheet;
  1169. path.push_back( pin->GetParent() );
  1170. if( !m_sheet_to_subgraphs_map.count( path ) )
  1171. continue;
  1172. for( auto candidate : m_sheet_to_subgraphs_map.at( path ) )
  1173. {
  1174. if( !candidate->m_strong_driver ||
  1175. candidate->m_hier_ports.empty() ||
  1176. visited.count( candidate ) )
  1177. continue;
  1178. for( SCH_HIERLABEL* label : candidate->m_hier_ports )
  1179. {
  1180. if( label->GetShownText() == pin->GetShownText() )
  1181. {
  1182. wxLogTrace( "CONN", "%lu: found child %lu (%s)", aParent->m_code,
  1183. candidate->m_code, candidate->m_driver_connection->Name() );
  1184. search_list.push_back( candidate );
  1185. break;
  1186. }
  1187. }
  1188. }
  1189. }
  1190. for( SCH_HIERLABEL* label : aParent->m_hier_ports )
  1191. {
  1192. SCH_SHEET_PATH path = aParent->m_sheet;
  1193. path.pop_back();
  1194. if( !m_sheet_to_subgraphs_map.count( path ) )
  1195. continue;
  1196. for( auto candidate : m_sheet_to_subgraphs_map.at( path ) )
  1197. {
  1198. if( candidate->m_hier_pins.empty() ||
  1199. visited.count( candidate ) ||
  1200. ( candidate->m_driver_connection->Type() !=
  1201. aParent->m_driver_connection->Type() ) )
  1202. continue;
  1203. for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
  1204. {
  1205. SCH_SHEET_PATH pin_path = path;
  1206. pin_path.push_back( pin->GetParent() );
  1207. if( pin_path != aParent->m_sheet )
  1208. continue;
  1209. if( label->GetShownText() == pin->GetShownText() )
  1210. {
  1211. wxLogTrace( "CONN", "%lu: found additional parent %lu (%s)",
  1212. aParent->m_code, candidate->m_code,
  1213. candidate->m_driver_connection->Name() );
  1214. search_list.push_back( candidate );
  1215. break;
  1216. }
  1217. }
  1218. }
  1219. }
  1220. };
  1221. auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
  1222. for( const auto& kv : aParentGraph->m_bus_neighbors )
  1223. {
  1224. for( CONNECTION_SUBGRAPH* neighbor : kv.second )
  1225. {
  1226. // May have been absorbed but won't have been deleted
  1227. if( neighbor->m_absorbed )
  1228. neighbor = neighbor->m_absorbed_by;
  1229. SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
  1230. // Now member may be out of date, since we just cloned the
  1231. // connection from higher up in the hierarchy. We need to
  1232. // figure out what the actual new connection is.
  1233. SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
  1234. // This is bad, probably an ERC error
  1235. if( !member )
  1236. {
  1237. wxLogTrace( "CONN", "Could not match bus member %s in %s",
  1238. kv.first->Name(), parent->Name() );
  1239. continue;
  1240. }
  1241. auto neighbor_conn = neighbor->m_driver_connection;
  1242. auto neighbor_name = neighbor_conn->Name();
  1243. if( neighbor_name == member->Name() )
  1244. continue;
  1245. wxLogTrace( "CONN", "%lu (%s) connected to bus member %s", neighbor->m_code,
  1246. neighbor_name, member->Name() );
  1247. neighbor_conn->Clone( *member );
  1248. neighbor->UpdateItemConnections();
  1249. recacheSubgraphName( neighbor, neighbor_name );
  1250. // Recurse onto this neighbor in case it needs to re-propagate
  1251. neighbor->m_dirty = true;
  1252. propagateToNeighbors( neighbor );
  1253. }
  1254. }
  1255. };
  1256. // If we don't have any hier pins (i.e. no children), nothing to do
  1257. if( aSubgraph->m_hier_pins.empty() )
  1258. {
  1259. // If we also don't have any parents, we'll never be visited again
  1260. if( aSubgraph->m_hier_ports.empty() )
  1261. aSubgraph->m_dirty = false;
  1262. return;
  1263. }
  1264. // If we do have hier ports, skip this subgraph as it will be visited by a parent
  1265. // TODO(JE) this will leave the subgraph dirty if there is no matching parent subgraph,
  1266. // which should be flagged as an ERC error
  1267. if( !aSubgraph->m_hier_ports.empty() )
  1268. return;
  1269. visited.insert( aSubgraph );
  1270. // If we are a bus, we must propagate to local neighbors and then the hierarchy
  1271. if( conn->IsBus() )
  1272. propagate_bus_neighbors( aSubgraph );
  1273. wxLogTrace( "CONN", "Propagating %lu (%s) to subsheets",
  1274. aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
  1275. visit( aSubgraph );
  1276. for( unsigned i = 0; i < search_list.size(); i++ )
  1277. {
  1278. auto child = search_list[i];
  1279. visited.insert( child );
  1280. visit( child );
  1281. child->m_dirty = false;
  1282. }
  1283. // Now, find the best driver for this chain of subgraphs
  1284. CONNECTION_SUBGRAPH* driver = aSubgraph;
  1285. CONNECTION_SUBGRAPH::PRIORITY highest =
  1286. CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver );
  1287. // Check if a subsheet has a higher-priority connection to the same net
  1288. if( highest < CONNECTION_SUBGRAPH::PRIORITY_POWER_PIN )
  1289. {
  1290. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1291. {
  1292. CONNECTION_SUBGRAPH::PRIORITY priority =
  1293. CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
  1294. // Upgrade driver to be this subgraph if this subgraph has a power pin or global
  1295. // Also upgrade if we found something with a shorter sheet path (higher in hierarchy)
  1296. // but with an equivalent priority
  1297. if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY_POWER_PIN ) ||
  1298. ( priority >= highest && subgraph->m_sheet.size() < aSubgraph->m_sheet.size() ) )
  1299. driver = subgraph;
  1300. }
  1301. }
  1302. if( driver != aSubgraph )
  1303. {
  1304. wxLogTrace( "CONN", "%lu (%s) overridden by new driver %lu (%s)",
  1305. aSubgraph->m_code, aSubgraph->m_driver_connection->Name(),
  1306. driver->m_code, driver->m_driver_connection->Name() );
  1307. }
  1308. conn = driver->m_driver_connection;
  1309. for( CONNECTION_SUBGRAPH* subgraph : visited )
  1310. {
  1311. wxString old_name = subgraph->m_driver_connection->Name();
  1312. subgraph->m_driver_connection->Clone( *conn );
  1313. subgraph->UpdateItemConnections();
  1314. recacheSubgraphName( subgraph, old_name );
  1315. if( conn->IsBus() )
  1316. propagate_bus_neighbors( subgraph );
  1317. }
  1318. aSubgraph->m_dirty = false;
  1319. }
  1320. SCH_CONNECTION* CONNECTION_GRAPH::matchBusMember(
  1321. SCH_CONNECTION* aBusConnection, SCH_CONNECTION* aSearch )
  1322. {
  1323. wxASSERT( aBusConnection->IsBus() );
  1324. SCH_CONNECTION* match = nullptr;
  1325. if( aBusConnection->Type() == CONNECTION_BUS )
  1326. {
  1327. // Vector bus: compare against index, because we allow the name
  1328. // to be different
  1329. for( const auto& bus_member : aBusConnection->Members() )
  1330. {
  1331. if( bus_member->VectorIndex() == aSearch->VectorIndex() )
  1332. {
  1333. match = bus_member.get();
  1334. break;
  1335. }
  1336. }
  1337. }
  1338. else
  1339. {
  1340. // Group bus
  1341. for( const auto& c : aBusConnection->Members() )
  1342. {
  1343. // Vector inside group: compare names, because for bus groups
  1344. // we expect the naming to be consistent across all usages
  1345. // TODO(JE) explain this in the docs
  1346. if( c->Type() == CONNECTION_BUS )
  1347. {
  1348. for( const auto& bus_member : c->Members() )
  1349. {
  1350. if( bus_member->RawName() == aSearch->RawName() )
  1351. {
  1352. match = bus_member.get();
  1353. break;
  1354. }
  1355. }
  1356. }
  1357. else if( c->RawName() == aSearch->RawName() )
  1358. {
  1359. match = c.get();
  1360. break;
  1361. }
  1362. }
  1363. }
  1364. return match;
  1365. }
  1366. void CONNECTION_GRAPH::recacheSubgraphName(
  1367. CONNECTION_SUBGRAPH* aSubgraph, const wxString& aOldName )
  1368. {
  1369. if( m_net_name_to_subgraphs_map.count( aOldName ) )
  1370. {
  1371. auto& vec = m_net_name_to_subgraphs_map.at( aOldName );
  1372. vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
  1373. }
  1374. m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
  1375. }
  1376. std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
  1377. {
  1378. if( m_bus_alias_cache.count( aName ) )
  1379. return m_bus_alias_cache.at( aName );
  1380. return nullptr;
  1381. }
  1382. std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
  1383. {
  1384. std::vector<const CONNECTION_SUBGRAPH*> ret;
  1385. for( auto&& subgraph : m_subgraphs )
  1386. {
  1387. // Graph is supposed to be up-to-date before calling this
  1388. wxASSERT( !subgraph->m_dirty );
  1389. if( !subgraph->m_driver )
  1390. continue;
  1391. auto sheet = subgraph->m_sheet;
  1392. auto connection = subgraph->m_driver->Connection( sheet );
  1393. if( !connection->IsBus() )
  1394. continue;
  1395. auto labels = subgraph->GetBusLabels();
  1396. if( labels.size() > 1 )
  1397. {
  1398. bool different = false;
  1399. wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetText();
  1400. for( unsigned i = 1; i < labels.size(); ++i )
  1401. {
  1402. if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetText() != first )
  1403. {
  1404. different = true;
  1405. break;
  1406. }
  1407. }
  1408. if( !different )
  1409. continue;
  1410. wxLogTrace( "CONN", "SG %ld (%s) has multiple bus labels", subgraph->m_code,
  1411. connection->Name() );
  1412. ret.push_back( subgraph );
  1413. }
  1414. }
  1415. return ret;
  1416. }
  1417. bool CONNECTION_GRAPH::UsesNewBusFeatures() const
  1418. {
  1419. for( auto&& subgraph : m_subgraphs )
  1420. {
  1421. if( !subgraph->m_driver )
  1422. continue;
  1423. auto sheet = subgraph->m_sheet;
  1424. auto connection = subgraph->m_driver->Connection( sheet );
  1425. if( !connection->IsBus() )
  1426. continue;
  1427. if( connection->Type() == CONNECTION_BUS_GROUP )
  1428. return true;
  1429. }
  1430. return false;
  1431. }
  1432. int CONNECTION_GRAPH::RunERC( const ERC_SETTINGS& aSettings, bool aCreateMarkers )
  1433. {
  1434. int error_count = 0;
  1435. for( auto&& subgraph : m_subgraphs )
  1436. {
  1437. // Graph is supposed to be up-to-date before calling RunERC()
  1438. wxASSERT( !subgraph->m_dirty );
  1439. /**
  1440. * NOTE:
  1441. *
  1442. * We could check that labels attached to bus subgraphs follow the
  1443. * proper format (i.e. actually define a bus).
  1444. *
  1445. * This check doesn't need to be here right now because labels
  1446. * won't actually be connected to bus wires if they aren't in the right
  1447. * format due to their TestDanglingEnds() implementation.
  1448. */
  1449. if( aSettings.check_bus_driver_conflicts &&
  1450. !subgraph->ResolveDrivers( aCreateMarkers ) )
  1451. error_count++;
  1452. if( aSettings.check_bus_to_net_conflicts &&
  1453. !ercCheckBusToNetConflicts( subgraph, aCreateMarkers ) )
  1454. error_count++;
  1455. if( aSettings.check_bus_entry_conflicts &&
  1456. !ercCheckBusToBusEntryConflicts( subgraph, aCreateMarkers ) )
  1457. error_count++;
  1458. if( aSettings.check_bus_to_bus_conflicts &&
  1459. !ercCheckBusToBusConflicts( subgraph, aCreateMarkers ) )
  1460. error_count++;
  1461. // The following checks are always performed since they don't currently
  1462. // have an option exposed to the user
  1463. if( !ercCheckNoConnects( subgraph, aCreateMarkers ) )
  1464. error_count++;
  1465. if( !ercCheckLabels( subgraph, aCreateMarkers, aSettings.check_unique_global_labels ) )
  1466. error_count++;
  1467. }
  1468. return error_count;
  1469. }
  1470. bool CONNECTION_GRAPH::ercCheckBusToNetConflicts( const CONNECTION_SUBGRAPH* aSubgraph,
  1471. bool aCreateMarkers )
  1472. {
  1473. wxString msg;
  1474. auto sheet = aSubgraph->m_sheet;
  1475. auto screen = sheet.LastScreen();
  1476. SCH_ITEM* net_item = nullptr;
  1477. SCH_ITEM* bus_item = nullptr;
  1478. SCH_CONNECTION conn;
  1479. for( auto item : aSubgraph->m_items )
  1480. {
  1481. switch( item->Type() )
  1482. {
  1483. case SCH_LINE_T:
  1484. {
  1485. if( item->GetLayer() == LAYER_BUS )
  1486. bus_item = ( !bus_item ) ? item : bus_item;
  1487. else
  1488. net_item = ( !net_item ) ? item : net_item;
  1489. break;
  1490. }
  1491. case SCH_GLOBAL_LABEL_T:
  1492. case SCH_SHEET_PIN_T:
  1493. case SCH_HIER_LABEL_T:
  1494. {
  1495. auto text = static_cast<SCH_TEXT*>( item )->GetShownText();
  1496. conn.ConfigureFromLabel( text );
  1497. if( conn.IsBus() )
  1498. bus_item = ( !bus_item ) ? item : bus_item;
  1499. else
  1500. net_item = ( !net_item ) ? item : net_item;
  1501. break;
  1502. }
  1503. default:
  1504. break;
  1505. }
  1506. }
  1507. if( net_item && bus_item )
  1508. {
  1509. if( aCreateMarkers )
  1510. {
  1511. msg.Printf( _( "%s and %s are graphically connected but cannot"
  1512. " electrically connect because one is a bus and"
  1513. " the other is a net." ),
  1514. bus_item->GetSelectMenuText( m_frame->GetUserUnits() ),
  1515. net_item->GetSelectMenuText( m_frame->GetUserUnits() ) );
  1516. auto marker = new SCH_MARKER();
  1517. marker->SetTimeStamp( GetNewTimeStamp() );
  1518. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1519. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
  1520. marker->SetData( ERCE_BUS_TO_NET_CONFLICT,
  1521. net_item->GetPosition(), msg,
  1522. bus_item->GetPosition() );
  1523. screen->Append( marker );
  1524. }
  1525. return false;
  1526. }
  1527. return true;
  1528. }
  1529. bool CONNECTION_GRAPH::ercCheckBusToBusConflicts( const CONNECTION_SUBGRAPH* aSubgraph,
  1530. bool aCreateMarkers )
  1531. {
  1532. wxString msg;
  1533. auto sheet = aSubgraph->m_sheet;
  1534. auto screen = sheet.LastScreen();
  1535. SCH_ITEM* label = nullptr;
  1536. SCH_ITEM* port = nullptr;
  1537. for( auto item : aSubgraph->m_items )
  1538. {
  1539. switch( item->Type() )
  1540. {
  1541. case SCH_TEXT_T:
  1542. case SCH_GLOBAL_LABEL_T:
  1543. {
  1544. if( !label && item->Connection( sheet )->IsBus() )
  1545. label = item;
  1546. break;
  1547. }
  1548. case SCH_SHEET_PIN_T:
  1549. case SCH_HIER_LABEL_T:
  1550. {
  1551. if( !port && item->Connection( sheet )->IsBus() )
  1552. port = item;
  1553. break;
  1554. }
  1555. default:
  1556. break;
  1557. }
  1558. }
  1559. if( label && port )
  1560. {
  1561. bool match = false;
  1562. for( const auto& member : label->Connection( sheet )->Members() )
  1563. {
  1564. for( const auto& test : port->Connection( sheet )->Members() )
  1565. {
  1566. if( test != member && member->Name() == test->Name() )
  1567. {
  1568. match = true;
  1569. break;
  1570. }
  1571. }
  1572. if( match )
  1573. break;
  1574. }
  1575. if( !match )
  1576. {
  1577. if( aCreateMarkers )
  1578. {
  1579. msg.Printf( _( "%s and %s are graphically connected but do "
  1580. "not share any bus members" ),
  1581. label->GetSelectMenuText( m_frame->GetUserUnits() ),
  1582. port->GetSelectMenuText( m_frame->GetUserUnits() ) );
  1583. auto marker = new SCH_MARKER();
  1584. marker->SetTimeStamp( GetNewTimeStamp() );
  1585. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1586. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
  1587. marker->SetData( ERCE_BUS_TO_BUS_CONFLICT,
  1588. label->GetPosition(), msg,
  1589. port->GetPosition() );
  1590. screen->Append( marker );
  1591. }
  1592. return false;
  1593. }
  1594. }
  1595. return true;
  1596. }
  1597. bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( const CONNECTION_SUBGRAPH* aSubgraph,
  1598. bool aCreateMarkers )
  1599. {
  1600. wxString msg;
  1601. bool conflict = false;
  1602. auto sheet = aSubgraph->m_sheet;
  1603. auto screen = sheet.LastScreen();
  1604. SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
  1605. SCH_ITEM* bus_wire = nullptr;
  1606. for( auto item : aSubgraph->m_items )
  1607. {
  1608. switch( item->Type() )
  1609. {
  1610. case SCH_BUS_WIRE_ENTRY_T:
  1611. {
  1612. if( !bus_entry )
  1613. bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  1614. break;
  1615. }
  1616. default:
  1617. break;
  1618. }
  1619. }
  1620. if( bus_entry && bus_entry->m_connected_bus_item )
  1621. {
  1622. bus_wire = bus_entry->m_connected_bus_item;
  1623. conflict = true;
  1624. auto test_name = bus_entry->Connection( sheet )->Name();
  1625. for( const auto& member : bus_wire->Connection( sheet )->Members() )
  1626. {
  1627. if( member->Type() == CONNECTION_BUS )
  1628. {
  1629. for( const auto& sub_member : member->Members() )
  1630. if( sub_member->Name() == test_name )
  1631. conflict = false;
  1632. }
  1633. else if( member->Name() == test_name )
  1634. {
  1635. conflict = false;
  1636. }
  1637. }
  1638. }
  1639. if( conflict )
  1640. {
  1641. if( aCreateMarkers )
  1642. {
  1643. msg.Printf( _( "%s (%s) is connected to %s (%s) but is not a member of the bus" ),
  1644. bus_entry->GetSelectMenuText( m_frame->GetUserUnits() ),
  1645. bus_entry->Connection( sheet )->Name(),
  1646. bus_wire->GetSelectMenuText( m_frame->GetUserUnits() ),
  1647. bus_wire->Connection( sheet )->Name() );
  1648. auto marker = new SCH_MARKER();
  1649. marker->SetTimeStamp( GetNewTimeStamp() );
  1650. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1651. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1652. marker->SetData( ERCE_BUS_ENTRY_CONFLICT,
  1653. bus_entry->GetPosition(), msg,
  1654. bus_entry->GetPosition() );
  1655. screen->Append( marker );
  1656. }
  1657. return false;
  1658. }
  1659. return true;
  1660. }
  1661. // TODO(JE) Check sheet pins here too?
  1662. bool CONNECTION_GRAPH::ercCheckNoConnects( const CONNECTION_SUBGRAPH* aSubgraph,
  1663. bool aCreateMarkers )
  1664. {
  1665. wxString msg;
  1666. auto sheet = aSubgraph->m_sheet;
  1667. auto screen = sheet.LastScreen();
  1668. if( aSubgraph->m_no_connect != nullptr )
  1669. {
  1670. bool has_invalid_items = false;
  1671. bool has_other_items = false;
  1672. SCH_PIN* pin = nullptr;
  1673. std::vector<SCH_ITEM*> invalid_items;
  1674. // Any subgraph that contains both a pin and a no-connect should not
  1675. // contain any other driving items.
  1676. for( auto item : aSubgraph->m_items )
  1677. {
  1678. switch( item->Type() )
  1679. {
  1680. case SCH_PIN_T:
  1681. pin = static_cast<SCH_PIN*>( item );
  1682. has_other_items = true;
  1683. break;
  1684. case SCH_LINE_T:
  1685. case SCH_JUNCTION_T:
  1686. case SCH_NO_CONNECT_T:
  1687. break;
  1688. default:
  1689. has_invalid_items = true;
  1690. has_other_items = true;
  1691. invalid_items.push_back( item );
  1692. }
  1693. }
  1694. if( pin && has_invalid_items )
  1695. {
  1696. if( aCreateMarkers )
  1697. {
  1698. wxPoint pos = pin->GetTransformedPosition();
  1699. msg.Printf( _( "Pin %s of component %s has a no-connect marker but is connected" ),
  1700. GetChars( pin->GetName() ),
  1701. GetChars( pin->GetParentComponent()->GetRef( &aSubgraph->m_sheet ) ) );
  1702. auto marker = new SCH_MARKER();
  1703. marker->SetTimeStamp( GetNewTimeStamp() );
  1704. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1705. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1706. marker->SetData( ERCE_NOCONNECT_CONNECTED, pos, msg, pos );
  1707. screen->Append( marker );
  1708. }
  1709. return false;
  1710. }
  1711. if( !has_other_items )
  1712. {
  1713. if( aCreateMarkers )
  1714. {
  1715. wxPoint pos = aSubgraph->m_no_connect->GetPosition();
  1716. msg.Printf( _( "No-connect marker is not connected to anything" ) );
  1717. auto marker = new SCH_MARKER();
  1718. marker->SetTimeStamp( GetNewTimeStamp() );
  1719. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1720. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1721. marker->SetData( ERCE_NOCONNECT_NOT_CONNECTED, pos, msg, pos );
  1722. screen->Append( marker );
  1723. }
  1724. return false;
  1725. }
  1726. }
  1727. else
  1728. {
  1729. bool has_other_connections = false;
  1730. SCH_PIN* pin = nullptr;
  1731. // Any subgraph that lacks a no-connect and contains a pin should also
  1732. // contain at least one other connectable item.
  1733. for( auto item : aSubgraph->m_items )
  1734. {
  1735. switch( item->Type() )
  1736. {
  1737. case SCH_PIN_T:
  1738. if( !pin )
  1739. pin = static_cast<SCH_PIN*>( item );
  1740. else
  1741. has_other_connections = true;
  1742. break;
  1743. default:
  1744. if( item->IsConnectable() )
  1745. has_other_connections = true;
  1746. break;
  1747. }
  1748. }
  1749. // Check if invisible power pins connect to anything else
  1750. // Note this won't catch if a component has multiple invisible power
  1751. // pins but these don't connect to any other net; maybe that should be
  1752. // added as a further optional ERC check.
  1753. if( pin && !has_other_connections &&
  1754. pin->IsPowerConnection() && !pin->IsVisible() )
  1755. {
  1756. wxString name = pin->Connection( sheet )->Name();
  1757. wxString local_name = pin->Connection( sheet )->Name( true );
  1758. if( m_global_label_cache.count( name ) ||
  1759. ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
  1760. {
  1761. has_other_connections = true;
  1762. }
  1763. }
  1764. if( pin && !has_other_connections && pin->GetType() != PIN_NC )
  1765. {
  1766. if( aCreateMarkers )
  1767. {
  1768. wxPoint pos = pin->GetTransformedPosition();
  1769. msg.Printf( _( "Pin %s of component %s is unconnected." ),
  1770. GetChars( pin->GetName() ),
  1771. GetChars( pin->GetParentComponent()->GetRef( &aSubgraph->m_sheet ) ) );
  1772. auto marker = new SCH_MARKER();
  1773. marker->SetTimeStamp( GetNewTimeStamp() );
  1774. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1775. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1776. marker->SetData( ERCE_PIN_NOT_CONNECTED, pos, msg, pos );
  1777. screen->Append( marker );
  1778. }
  1779. return false;
  1780. }
  1781. }
  1782. return true;
  1783. }
  1784. bool CONNECTION_GRAPH::ercCheckLabels( const CONNECTION_SUBGRAPH* aSubgraph,
  1785. bool aCreateMarkers, bool aCheckGlobalLabels )
  1786. {
  1787. // Label connection rules:
  1788. // Local labels are flagged if they don't connect to any pins and don't have a no-connect
  1789. // Global labels are flagged if they appear only once, don't connect to any local labels,
  1790. // and don't have a no-connect marker
  1791. // So, if there is a no-connect, we will never generate a warning here
  1792. if( aSubgraph->m_no_connect )
  1793. return true;
  1794. SCH_TEXT* text = nullptr;
  1795. bool has_other_connections = false;
  1796. for( auto item : aSubgraph->m_items )
  1797. {
  1798. switch( item->Type() )
  1799. {
  1800. case SCH_LABEL_T:
  1801. case SCH_GLOBAL_LABEL_T:
  1802. case SCH_HIER_LABEL_T:
  1803. text = static_cast<SCH_TEXT*>( item );
  1804. break;
  1805. case SCH_PIN_T:
  1806. case SCH_SHEET_PIN_T:
  1807. has_other_connections = true;
  1808. break;
  1809. default:
  1810. break;
  1811. }
  1812. }
  1813. if( !text )
  1814. return true;
  1815. bool is_global = text->Type() == SCH_GLOBAL_LABEL_T;
  1816. // Global label check can be disabled independently
  1817. if( !aCheckGlobalLabels && is_global )
  1818. return true;
  1819. wxString name = text->GetShownText();
  1820. if( is_global)
  1821. {
  1822. // This will be set to true if the global is connected to a pin above, but we
  1823. // want to reset this to false so that globals get flagged if they only have a
  1824. // single instance
  1825. has_other_connections = false;
  1826. if( m_net_name_to_subgraphs_map.count( name )
  1827. && m_net_name_to_subgraphs_map.at( name ).size() > 1 )
  1828. has_other_connections = true;
  1829. }
  1830. else
  1831. {
  1832. auto pair = std::make_pair( aSubgraph->m_sheet, name );
  1833. if( m_local_label_cache.count( pair ) && m_local_label_cache.at( pair ).size() > 1 )
  1834. has_other_connections = true;
  1835. }
  1836. if( !has_other_connections )
  1837. {
  1838. if( aCreateMarkers )
  1839. {
  1840. SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
  1841. wxPoint pos = text->GetPosition();
  1842. auto marker = new SCH_MARKER();
  1843. wxString msg;
  1844. wxString prefix = is_global ? _( "Global label" ) : _( "Label" );
  1845. ERCE_T type = is_global ? ERCE_GLOBLABEL : ERCE_LABEL_NOT_CONNECTED;
  1846. msg.Printf( _( "%s %s is not connected anywhere else in the schematic." ),
  1847. prefix, GetChars( text->ShortenedShownText() ) );
  1848. marker->SetTimeStamp( GetNewTimeStamp() );
  1849. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1850. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1851. marker->SetData( type, pos, msg, pos );
  1852. screen->Append( marker );
  1853. }
  1854. return false;
  1855. }
  1856. return true;
  1857. }