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