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