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