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