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  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Jon Evans <jon@craftyjon.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <list>
24 #include <thread>
25 #include <algorithm>
26 #include <future>
27 #include <vector>
28 #include <unordered_map>
29 #include <profile.h>
30 #include <common.h>
31 #include <erc.h>
32 #include <pin_type.h>
33 #include <sch_bus_entry.h>
34 #include <sch_symbol.h>
35 #include <sch_edit_frame.h>
36 #include <sch_line.h>
37 #include <sch_marker.h>
38 #include <sch_pin.h>
39 #include <sch_sheet.h>
40 #include <sch_sheet_path.h>
41 #include <sch_sheet_pin.h>
42 #include <sch_text.h>
43 #include <schematic.h>
44 #include <connection_graph.h>
45 #include <widgets/ui_common.h>
46 #include <kicad_string.h>
47 #include <wx/log.h>
48 
49 #include <advanced_config.h> // for realtime connectivity switch
50 
51 
52 /*
53  * Flag to enable connectivity profiling
54  * @ingroup trace_env_vars
55  */
56 static const wxChar ConnProfileMask[] = wxT( "CONN_PROFILE" );
57 
58 /*
59  * Flag to enable connectivity tracing
60  * @ingroup trace_env_vars
61  */
62 static const wxChar ConnTrace[] = wxT( "CONN" );
63 
64 
65 bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers )
66 {
67  PRIORITY highest_priority = PRIORITY::INVALID;
68  std::vector<SCH_ITEM*> candidates;
69  std::vector<SCH_ITEM*> strong_drivers;
70 
71  m_driver = nullptr;
72 
73  // Hierarchical labels are lower priority than local labels here,
74  // because on the first pass we want local labels to drive subgraphs
75  // so that we can identify same-sheet neighbors and link them together.
76  // Hierarchical labels will end up overriding the final net name if
77  // a higher-level sheet has a different name during the hierarchical
78  // pass.
79 
80  for( SCH_ITEM* item : m_drivers )
81  {
82  PRIORITY item_priority = GetDriverPriority( item );
83 
84  if( item_priority == PRIORITY::PIN
85  && !static_cast<SCH_PIN*>( item )->GetParentSymbol()->IsInNetlist() )
86  continue;
87 
88  if( item_priority >= PRIORITY::HIER_LABEL )
89  strong_drivers.push_back( item );
90 
91  if( item_priority > highest_priority )
92  {
93  candidates.clear();
94  candidates.push_back( item );
95  highest_priority = item_priority;
96  }
97  else if( !candidates.empty() && ( item_priority == highest_priority ) )
98  {
99  candidates.push_back( item );
100  }
101  }
102 
103  if( highest_priority >= PRIORITY::HIER_LABEL )
104  m_strong_driver = true;
105 
106  // Power pins are 5, global labels are 6
107  m_local_driver = ( highest_priority < PRIORITY::POWER_PIN );
108 
109  if( !candidates.empty() )
110  {
111  if( candidates.size() > 1 )
112  {
113  if( highest_priority == PRIORITY::SHEET_PIN )
114  {
115  // We have multiple options, and they are all hierarchical
116  // sheet pins. Let's prefer outputs over inputs.
117 
118  for( SCH_ITEM* c : candidates )
119  {
120  SCH_SHEET_PIN* p = static_cast<SCH_SHEET_PIN*>( c );
121 
123  {
124  m_driver = c;
125  break;
126  }
127  }
128  }
129  else
130  {
131  // See if a previous driver is still a candidate
132  void* previousDriver = nullptr;
133 
134  for( SCH_ITEM* member : m_items )
135  {
136  if( SCH_CONNECTION* mc = member->Connection( &m_sheet ) )
137  {
138  if( mc->GetLastDriver() )
139  {
140  previousDriver = mc->GetLastDriver();
141  break;
142  }
143  }
144  }
145 
146  // For all other driver types, sort by name
147  std::sort( candidates.begin(), candidates.end(),
148  [&]( SCH_ITEM* a, SCH_ITEM* b ) -> bool
149  {
150  // meet irreflexive requirements of std::sort
151  if( a == b )
152  return false;
153 
154  SCH_CONNECTION* ac = a->Connection( &m_sheet );
155  SCH_CONNECTION* bc = b->Connection( &m_sheet );
156 
157  // Ensure we don't pick the subset over the superset
158  if( ac->IsBus() && bc->IsBus() )
159  return bc->IsSubsetOf( ac );
160 
161  if( a == previousDriver )
162  return true;
163  else if( b == previousDriver )
164  return false;
165  else
166  return GetNameForDriver( a ) < GetNameForDriver( b );
167  } );
168  }
169  }
170 
171  if( !m_driver )
172  m_driver = candidates[0];
173  }
174 
175  if( strong_drivers.size() > 1 )
176  m_multiple_drivers = true;
177 
178  // Drop weak drivers
179  if( m_strong_driver )
180  m_drivers = strong_drivers;
181 
182  // Cache driver connection
183  if( m_driver )
184  {
189  }
190  else
191  {
192  m_driver_connection = nullptr;
193  }
194 
195  if( aCheckMultipleDrivers && m_multiple_drivers )
196  {
197  // First check if all the candidates are actually the same
198  bool same = true;
199  wxString first = GetNameForDriver( candidates[0] );
200  SCH_ITEM* second_item = nullptr;
201 
202  for( unsigned i = 1; i < candidates.size(); i++ )
203  {
204  if( GetNameForDriver( candidates[i] ) != first )
205  {
206  second_item = candidates[i];
207  same = false;
208  break;
209  }
210  }
211 
212  if( !same )
213  {
215  m_second_driver = second_item;
216  }
217  }
218 
219  return ( m_driver != nullptr );
220 }
221 
222 
224 {
225  if( !m_driver || m_dirty )
226  return "";
227 
228  if( !m_driver->Connection( &m_sheet ) )
229  {
230 #ifdef CONNECTIVITY_DEBUG
231  wxASSERT_MSG( false, "Tried to get the net name of an item with no connection" );
232 #endif
233 
234  return "";
235  }
236 
237  return m_driver->Connection( &m_sheet )->Name();
238 }
239 
240 
241 std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetBusLabels() const
242 {
243  std::vector<SCH_ITEM*> labels;
244 
245  for( SCH_ITEM* item : m_drivers )
246  {
247  switch( item->Type() )
248  {
249  case SCH_LABEL_T:
250  case SCH_GLOBAL_LABEL_T:
251  {
252  SCH_CONNECTION* label_conn = item->Connection( &m_sheet );
253 
254  // Only consider bus vectors
255  if( label_conn->Type() == CONNECTION_TYPE::BUS )
256  labels.push_back( item );
257 
258  break;
259  }
260 
261  default: break;
262  }
263  }
264 
265  return labels;
266 }
267 
268 
270 {
271  switch( aItem->Type() )
272  {
273  case SCH_PIN_T:
274  {
275  bool forceNoConnect = m_no_connect != nullptr;
276  SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
277  return pin->GetDefaultNetName( m_sheet, forceNoConnect );
278  break;
279  }
280 
281  case SCH_LABEL_T:
282  case SCH_GLOBAL_LABEL_T:
283  case SCH_HIER_LABEL_T:
284  case SCH_SHEET_PIN_T:
285  {
286  return EscapeString( static_cast<SCH_TEXT*>( aItem )->GetShownText(), CTX_NETNAME );
287  break;
288  }
289 
290  default:
291  wxFAIL_MSG( "Unhandled item type in GetNameForDriver" );
292  break;
293  }
294 
295  return wxEmptyString;
296 }
297 
298 
300 {
301  auto it = m_driver_name_cache.find( aItem );
302 
303  if( it != m_driver_name_cache.end() )
304  return it->second;
305 
306  m_driver_name_cache[aItem] = driverName( aItem );
307 
308  return m_driver_name_cache.at( aItem );
309 }
310 
311 
312 const wxString CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem ) const
313 {
314  auto it = m_driver_name_cache.find( aItem );
315 
316  if( it != m_driver_name_cache.end() )
317  return it->second;
318 
319  return driverName( aItem );
320 }
321 
322 
324 {
325  wxASSERT( m_sheet == aOther->m_sheet );
326 
327  for( SCH_ITEM* item : aOther->m_items )
328  {
330  AddItem( item );
331  }
332 
333  m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
334  m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
335 
337 
338  aOther->m_absorbed = true;
339  aOther->m_dirty = false;
340  aOther->m_driver = nullptr;
341  aOther->m_driver_connection = nullptr;
342  aOther->m_absorbed_by = this;
343 }
344 
345 
347 {
348  m_items.push_back( aItem );
349 
350  if( aItem->Connection( &m_sheet )->IsDriver() )
351  m_drivers.push_back( aItem );
352 
353  if( aItem->Type() == SCH_SHEET_PIN_T )
354  m_hier_pins.push_back( static_cast<SCH_SHEET_PIN*>( aItem ) );
355  else if( aItem->Type() == SCH_HIER_LABEL_T )
356  m_hier_ports.push_back( static_cast<SCH_HIERLABEL*>( aItem ) );
357 }
358 
359 
361 {
362  if( !m_driver_connection )
363  return;
364 
365  for( SCH_ITEM* item : m_items )
366  {
367  SCH_CONNECTION* item_conn = item->Connection( &m_sheet );
368 
369  if( !item_conn )
370  item_conn = item->InitializeConnection( m_sheet, m_graph );
371 
372  if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
373  ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
374  {
375  continue;
376  }
377 
378  if( item != m_driver )
379  {
380  item_conn->Clone( *m_driver_connection );
381  item_conn->ClearDirty();
382  }
383  }
384 }
385 
386 
388 {
389  if( !aDriver )
390  return PRIORITY::NONE;
391 
392  switch( aDriver->Type() )
393  {
396  case SCH_LABEL_T: return PRIORITY::LOCAL_LABEL;
398  case SCH_PIN_T:
399  {
400  auto sch_pin = static_cast<SCH_PIN*>( aDriver );
401 
402  if( sch_pin->IsPowerConnection() )
403  return PRIORITY::POWER_PIN;
404  else
405  return PRIORITY::PIN;
406  }
407 
408  default: return PRIORITY::NONE;
409  }
410 }
411 
412 
414 
415 
417 {
418  for( auto& subgraph : m_subgraphs )
419  delete subgraph;
420 
421  m_items.clear();
422  m_subgraphs.clear();
423  m_driver_subgraphs.clear();
424  m_sheet_to_subgraphs_map.clear();
425  m_invisible_power_pins.clear();
426  m_bus_alias_cache.clear();
427  m_net_name_to_code_map.clear();
428  m_bus_name_to_code_map.clear();
431  m_item_to_subgraph_map.clear();
432  m_local_label_cache.clear();
433  m_global_label_cache.clear();
434  m_last_net_code = 1;
435  m_last_bus_code = 1;
437 }
438 
439 
440 void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional,
441  std::function<void( SCH_ITEM* )>* aChangedItemHandler )
442 {
443  PROF_COUNTER recalc_time( "CONNECTION_GRAPH::Recalculate" );
444 
445  if( aUnconditional )
446  Reset();
447 
448  PROF_COUNTER update_items( "updateItemConnectivity" );
449 
450  m_sheetList = aSheetList;
451 
452  for( const SCH_SHEET_PATH& sheet : aSheetList )
453  {
454  std::vector<SCH_ITEM*> items;
455 
456  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
457  {
458  if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
459  items.push_back( item );
460  }
461 
462  m_items.reserve( m_items.size() + items.size() );
463 
464  updateItemConnectivity( sheet, items );
465 
466  // UpdateDanglingState() also adds connected items for SCH_TEXT
467  sheet.LastScreen()->TestDanglingEnds( &sheet, aChangedItemHandler );
468  }
469 
470  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
471  update_items.Show();
472 
473  PROF_COUNTER build_graph( "buildConnectionGraph" );
474 
476 
477  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
478  build_graph.Show();
479 
480  recalc_time.Stop();
481 
482  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
483  recalc_time.Show();
484 
485 #ifndef DEBUG
486  // Pressure relief valve for release builds
487  const double max_recalc_time_msecs = 250.;
488 
489  if( m_allowRealTime && ADVANCED_CFG::GetCfg().m_RealTimeConnectivity &&
490  recalc_time.msecs() > max_recalc_time_msecs )
491  {
492  m_allowRealTime = false;
493  }
494 #endif
495 }
496 
497 
499  const std::vector<SCH_ITEM*>& aItemList )
500 {
501  std::map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
502 
503  for( SCH_ITEM* item : aItemList )
504  {
505  std::vector< wxPoint > points = item->GetConnectionPoints();
506  item->ConnectedItems( aSheet ).clear();
507 
508  if( item->Type() == SCH_SHEET_T )
509  {
510  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
511  {
512  pin->InitializeConnection( aSheet, this );
513 
514  pin->ConnectedItems( aSheet ).clear();
515 
516  connection_map[ pin->GetTextPos() ].push_back( pin );
517  m_items.emplace_back( pin );
518  }
519  }
520  else if( item->Type() == SCH_SYMBOL_T )
521  {
522  SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
523 
524  for( SCH_PIN* pin : symbol->GetPins( &aSheet ) )
525  {
526  pin->InitializeConnection( aSheet, this );
527 
528  wxPoint pos = pin->GetPosition();
529 
530  // because calling the first time is not thread-safe
531  pin->GetDefaultNetName( aSheet );
532  pin->ConnectedItems( aSheet ).clear();
533 
534  // Invisible power pins need to be post-processed later
535 
536  if( pin->IsPowerConnection() && !pin->IsVisible() )
537  m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
538 
539  connection_map[ pos ].push_back( pin );
540  m_items.emplace_back( pin );
541  }
542  }
543  else
544  {
545  m_items.emplace_back( item );
546  auto conn = item->InitializeConnection( aSheet, this );
547 
548  // Set bus/net property here so that the propagation code uses it
549  switch( item->Type() )
550  {
551  case SCH_LINE_T:
552  conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
554  break;
555 
556  case SCH_BUS_BUS_ENTRY_T:
557  conn->SetType( CONNECTION_TYPE::BUS );
558  // clean previous (old) links:
559  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
560  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
561  break;
562 
563  case SCH_PIN_T:
564  conn->SetType( CONNECTION_TYPE::NET );
565  break;
566 
568  conn->SetType( CONNECTION_TYPE::NET );
569  // clean previous (old) link:
570  static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
571  break;
572 
573  default:
574  break;
575  }
576 
577  for( const wxPoint& point : points )
578  connection_map[ point ].push_back( item );
579  }
580 
581  item->SetConnectivityDirty( false );
582  }
583 
584  for( const auto& it : connection_map )
585  {
586  auto connection_vec = it.second;
587 
588  for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
589  {
590  SCH_ITEM* connected_item = *primary_it;
591 
592  // Bus entries are special: they can have connection points in the
593  // middle of a wire segment, because the junction algo doesn't split
594  // the segment in two where you place a bus entry. This means that
595  // bus entries that don't land on the end of a line segment need to
596  // have "virtual" connection points to the segments they graphically
597  // touch.
598  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
599  {
600  // If this location only has the connection point of the bus
601  // entry itself, this means that either the bus entry is not
602  // connected to anything graphically, or that it is connected to
603  // a segment at some point other than at one of the endpoints.
604  if( connection_vec.size() == 1 )
605  {
606  SCH_SCREEN* screen = aSheet.LastScreen();
607  SCH_LINE* bus = screen->GetBus( it.first );
608 
609  if( bus )
610  {
611  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
612  bus_entry->m_connected_bus_item = bus;
613  }
614  }
615  }
616 
617  // Bus-to-bus entries are treated just like bus wires
618  else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
619  {
620  if( connection_vec.size() < 2 )
621  {
622  SCH_SCREEN* screen = aSheet.LastScreen();
623  SCH_LINE* bus = screen->GetBus( it.first );
624 
625  if( bus )
626  {
627  auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
628 
629  if( it.first == bus_entry->GetPosition() )
630  bus_entry->m_connected_bus_items[0] = bus;
631  else
632  bus_entry->m_connected_bus_items[1] = bus;
633 
634  bus_entry->ConnectedItems( aSheet ).insert( bus );
635  bus->ConnectedItems( aSheet ).insert( bus_entry );
636  }
637  }
638  }
639 
640  // Change junctions to be on bus junction layer if they are touching a bus
641  else if( connected_item->Type() == SCH_JUNCTION_T )
642  {
643  SCH_SCREEN* screen = aSheet.LastScreen();
644  SCH_LINE* bus = screen->GetBus( it.first );
645 
646  connected_item->SetLayer( bus ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
647  }
648 
649  for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
650  {
651  auto test_item = *test_it;
652 
653  if( connected_item != test_item &&
654  connected_item->ConnectionPropagatesTo( test_item ) &&
655  test_item->ConnectionPropagatesTo( connected_item ) )
656  {
657  connected_item->ConnectedItems( aSheet ).insert( test_item );
658  test_item->ConnectedItems( aSheet ).insert( connected_item );
659  }
660 
661  // Set up the link between the bus entry net and the bus
662  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
663  {
664  if( test_item->Connection( &aSheet )->IsBus() )
665  {
666  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
667  bus_entry->m_connected_bus_item = test_item;
668  }
669  }
670  }
671 
672  // If we got this far and did not find a connected bus item for a bus entry,
673  // we should do a manual scan in case there is a bus item on this connection
674  // point but we didn't pick it up earlier because there is *also* a net item here.
675  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
676  {
677  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
678 
679  if( !bus_entry->m_connected_bus_item )
680  {
681  auto screen = aSheet.LastScreen();
682  auto bus = screen->GetBus( it.first );
683 
684  if( bus )
685  bus_entry->m_connected_bus_item = bus;
686  }
687  }
688  }
689  }
690 }
691 
692 
693 // TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
694 // to the same subgraph necessarily if it runs over and over again on the same
695 // sheet. We need:
696 //
697 // a) a cache of net/bus codes, like used before
698 // b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
699 // c) some way of trying to avoid changing net names. so we should keep track
700 // of the previous driver of a net, and if it comes down to choosing between
701 // equally-prioritized drivers, choose the one that already exists as a driver
702 // on some portion of the items.
703 
704 
706 {
707  // Recache all bus aliases for later use
708  wxCHECK_RET( m_schematic, "Connection graph cannot be built without schematic pointer" );
709 
710  SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
711 
712  for( unsigned i = 0; i < all_sheets.size(); i++ )
713  {
714  for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() )
715  m_bus_alias_cache[ alias->GetName() ] = alias;
716  }
717 
718  // Build subgraphs from items (on a per-sheet basis)
719 
720  for( SCH_ITEM* item : m_items )
721  {
722  for( const auto& it : item->m_connection_map )
723  {
724  const auto sheet = it.first;
725  auto connection = it.second;
726 
727  if( connection->SubgraphCode() == 0 )
728  {
729  CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
730 
731  subgraph->m_code = m_last_subgraph_code++;
732  subgraph->m_sheet = sheet;
733 
734  subgraph->AddItem( item );
735 
736  connection->SetSubgraphCode( subgraph->m_code );
737  m_item_to_subgraph_map[item] = subgraph;
738 
739  std::list<SCH_ITEM*> members;
740 
741  auto get_items =
742  [&]( SCH_ITEM* aItem ) -> bool
743  {
744  SCH_CONNECTION* conn = aItem->Connection( &sheet );
745 
746  if( !conn )
747  conn = aItem->InitializeConnection( sheet, this );
748 
749  return ( conn->SubgraphCode() == 0 );
750  };
751 
752  std::copy_if( item->ConnectedItems( sheet ).begin(),
753  item->ConnectedItems( sheet ).end(),
754  std::back_inserter( members ), get_items );
755 
756  for( SCH_ITEM* connected_item : members )
757  {
758  if( connected_item->Type() == SCH_NO_CONNECT_T )
759  subgraph->m_no_connect = connected_item;
760 
761  SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
762 
763  wxASSERT( connected_conn );
764 
765  if( connected_conn->SubgraphCode() == 0 )
766  {
767  connected_conn->SetSubgraphCode( subgraph->m_code );
768  m_item_to_subgraph_map[connected_item] = subgraph;
769  subgraph->AddItem( connected_item );
770 
771  std::copy_if( connected_item->ConnectedItems( sheet ).begin(),
772  connected_item->ConnectedItems( sheet ).end(),
773  std::back_inserter( members ), get_items );
774  }
775  }
776 
777  subgraph->m_dirty = true;
778  m_subgraphs.push_back( subgraph );
779  }
780  }
781  }
782 
788  // Resolve drivers for subgraphs and propagate connectivity info
789 
790  // We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
791  size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
792  ( m_subgraphs.size() + 3 ) / 4 );
793 
794  std::atomic<size_t> nextSubgraph( 0 );
795  std::vector<std::future<size_t>> returns( parallelThreadCount );
796  std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
797 
798  std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
799  [&] ( const CONNECTION_SUBGRAPH* candidate )
800  {
801  return candidate->m_dirty;
802  } );
803 
804  auto update_lambda = [&nextSubgraph, &dirty_graphs]() -> size_t
805  {
806  for( size_t subgraphId = nextSubgraph++; subgraphId < dirty_graphs.size(); subgraphId = nextSubgraph++ )
807  {
808  auto subgraph = dirty_graphs[subgraphId];
809 
810  if( !subgraph->m_dirty )
811  continue;
812 
813  // Special processing for some items
814  for( auto item : subgraph->m_items )
815  {
816  switch( item->Type() )
817  {
818  case SCH_NO_CONNECT_T:
819  subgraph->m_no_connect = item;
820  break;
821 
823  subgraph->m_bus_entry = item;
824  break;
825 
826  case SCH_PIN_T:
827  {
828  auto pin = static_cast<SCH_PIN*>( item );
829 
830  if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
831  subgraph->m_no_connect = item;
832 
833  break;
834  }
835 
836  default:
837  break;
838  }
839  }
840 
841  subgraph->ResolveDrivers( true );
842  subgraph->m_dirty = false;
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  // For vector buses, we need to cache the prefix also, as two different instances of the
879  // weakly driven pin may have the same prefix but different vector start and end. We need
880  // to treat those as needing renaming also, because otherwise if they end up on a sheet with
881  // common usage, they will be incorrectly merged.
882  if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
883  {
884  wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
885  m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
886  }
887 
888  subgraph->m_dirty = true;
889 
890  if( subgraph->m_strong_driver )
891  {
892  SCH_ITEM* driver = subgraph->m_driver;
893  SCH_SHEET_PATH sheet = subgraph->m_sheet;
894 
895  switch( driver->Type() )
896  {
897  case SCH_LABEL_T:
898  case SCH_HIER_LABEL_T:
899  {
900  m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
901  break;
902  }
903  case SCH_GLOBAL_LABEL_T:
904  {
905  m_global_label_cache[name].push_back( subgraph );
906  break;
907  }
908  case SCH_PIN_T:
909  {
910  auto pin = static_cast<SCH_PIN*>( driver );
911  wxASSERT( pin->IsPowerConnection() );
912  m_global_label_cache[name].push_back( subgraph );
913  break;
914  }
915  default:
916  wxLogTrace( ConnTrace, "Unexpected strong driver %s",
918  break;
919  }
920  }
921  }
922 
923  // Generate subgraphs for invisible power pins. These will be merged with other subgraphs
924  // on the same sheet in the next loop.
925 
926  std::unordered_map<int, CONNECTION_SUBGRAPH*> invisible_pin_subgraphs;
927 
928  for( const auto& it : m_invisible_power_pins )
929  {
930  SCH_SHEET_PATH sheet = it.first;
931  SCH_PIN* pin = it.second;
932 
933  if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParent()->IsPower() )
934  {
935  // ERC will warn about this: user has wired up an invisible pin
936  continue;
937  }
938 
939  SCH_CONNECTION* connection = pin->Connection( &sheet );
940 
941  if( !connection )
942  connection = pin->InitializeConnection( sheet, this );
943 
944  // If this pin already has a subgraph, don't need to process
945  if( connection->SubgraphCode() > 0 )
946  continue;
947 
948  connection->SetName( pin->GetShownName() );
949 
950  int code = assignNewNetCode( *connection );
951 
952  connection->SetNetCode( code );
953 
954  CONNECTION_SUBGRAPH* subgraph;
955  auto jj = invisible_pin_subgraphs.find( code );
956 
957  if( jj != invisible_pin_subgraphs.end() )
958  {
959  subgraph = jj->second;
960  subgraph->AddItem( pin );
961  }
962  else
963  {
964  subgraph = new CONNECTION_SUBGRAPH( this );
965 
966  subgraph->m_code = m_last_subgraph_code++;
967  subgraph->m_sheet = sheet;
968 
969  subgraph->AddItem( pin );
970  subgraph->ResolveDrivers();
971 
972  auto key = std::make_pair( subgraph->GetNetName(), code );
973  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
974  m_subgraphs.push_back( subgraph );
975  m_driver_subgraphs.push_back( subgraph );
976 
977  invisible_pin_subgraphs[code] = subgraph;
978  }
979 
980  connection->SetSubgraphCode( subgraph->m_code );
981  }
982 
983  // Here we do all the local (sheet) processing of each subgraph, including assigning net
984  // codes, merging subgraphs together that use label connections, etc.
985 
986  // Cache remaining valid subgraphs by sheet path
987  for( auto subgraph : m_driver_subgraphs )
988  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
989 
990  std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
991 
992  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
993  {
994  if( subgraph->m_absorbed )
995  continue;
996 
997  SCH_CONNECTION* connection = subgraph->m_driver_connection;
998  SCH_SHEET_PATH sheet = subgraph->m_sheet;
999  wxString name = connection->Name();
1000 
1001  // Test subgraphs with weak drivers for net name conflicts and fix them
1002  unsigned suffix = 1;
1003 
1004  auto create_new_name =
1005  [&suffix]( SCH_CONNECTION* aConn ) -> wxString
1006  {
1007  wxString newName;
1008 
1009  // For group buses with a prefix, we can add the suffix to the prefix.
1010  // If they don't have a prefix, we force the creation of a prefix so that
1011  // two buses don't get inadvertently shorted together.
1012  if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
1013  {
1014  wxString prefix = aConn->BusPrefix();
1015 
1016  if( prefix.empty() )
1017  prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
1018 
1019  wxString oldName = aConn->Name().AfterFirst( '{' );
1020 
1021  newName = wxString::Format( "%s_%u{%s", prefix, suffix, oldName );
1022 
1023  aConn->ConfigureFromLabel( newName );
1024  }
1025  else
1026  {
1027  newName = wxString::Format( "%s_%u", aConn->Name(), suffix );
1028  aConn->SetSuffix( wxString::Format( "_%u", suffix ) );
1029  }
1030 
1031  suffix++;
1032  return newName;
1033  };
1034 
1035  if( !subgraph->m_strong_driver )
1036  {
1037  std::vector<CONNECTION_SUBGRAPH*>* vec = &m_net_name_to_subgraphs_map.at( name );
1038 
1039  // If we are a unique bus vector, check if we aren't actually unique because of another
1040  // subgraph with a similar bus vector
1041  if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1042  {
1043  wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
1044  vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
1045  }
1046 
1047  if( vec->size() > 1 )
1048  {
1049  wxString new_name = create_new_name( connection );
1050 
1051  while( m_net_name_to_subgraphs_map.count( new_name ) )
1052  new_name = create_new_name( connection );
1053 
1054  wxLogTrace( ConnTrace, "%ld (%s) is weakly driven and not unique. Changing to %s.",
1055  subgraph->m_code, name, new_name );
1056 
1057  vec->erase( std::remove( vec->begin(), vec->end(), subgraph ), vec->end() );
1058 
1059  m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1060 
1061  name = new_name;
1062  }
1063  else
1064  {
1065  // If there is no conflict, promote sheet pins to be strong drivers so that they
1066  // will be considered below for propagation/merging.
1067 
1068  // It is possible for this to generate a conflict if the sheet pin has the same
1069  // name as a global label on the same sheet, because global merging will then treat
1070  // this subgraph as if it had a matching local label. So, for those cases, we
1071  // don't apply this promotion
1072 
1073  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1074  {
1075  bool conflict = false;
1076  wxString global_name = connection->Name( true );
1077  auto kk = m_net_name_to_subgraphs_map.find( global_name );
1078 
1079  if( kk != m_net_name_to_subgraphs_map.end() )
1080  {
1081  // A global will conflict if it is on the same sheet as this subgraph, since
1082  // it would be connected by implicit local label linking
1083  std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
1084 
1085  for( const CONNECTION_SUBGRAPH* candidate : candidates )
1086  {
1087  if( candidate->m_sheet == sheet )
1088  conflict = true;
1089  }
1090  }
1091 
1092  if( conflict )
1093  {
1094  wxLogTrace( ConnTrace,
1095  "%ld (%s) skipped for promotion due to potential conflict",
1096  subgraph->m_code, name );
1097  }
1098  else
1099  {
1100  wxLogTrace( ConnTrace,
1101  "%ld (%s) weakly driven by unique sheet pin %s, promoting",
1102  subgraph->m_code, name,
1103  subgraph->m_driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ) );
1104 
1105  subgraph->m_strong_driver = true;
1106  }
1107  }
1108  }
1109  }
1110 
1111  // Assign net codes
1112 
1113  if( connection->IsBus() )
1114  {
1115  int code = -1;
1116  auto it = m_bus_name_to_code_map.find( name );
1117 
1118  if( it != m_bus_name_to_code_map.end() )
1119  {
1120  code = it->second;
1121  }
1122  else
1123  {
1124  code = m_last_bus_code++;
1125  m_bus_name_to_code_map[ name ] = code;
1126  }
1127 
1128  connection->SetBusCode( code );
1129  assignNetCodesToBus( connection );
1130  }
1131  else
1132  {
1133  assignNewNetCode( *connection );
1134  }
1135 
1136  // Reset the flag for the next loop below
1137  subgraph->m_dirty = true;
1138 
1139  // Next, we merge together subgraphs that have label connections, and create
1140  // neighbor links for subgraphs that are part of a bus on the same sheet.
1141  // For merging, we consider each possible strong driver.
1142 
1143  // If this subgraph doesn't have a strong driver, let's skip it, since there is no
1144  // way it will be merged with anything.
1145 
1146  if( !subgraph->m_strong_driver )
1147  continue;
1148 
1149  // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
1150  // as the subgraph we are considering that has a strong driver.
1151  // Weakly driven subgraphs are not considered since they will never be absorbed or
1152  // form neighbor links.
1153 
1154  std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
1155  std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
1156  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
1157  std::back_inserter( candidate_subgraphs ),
1158  [&] ( const CONNECTION_SUBGRAPH* candidate )
1159  {
1160  return ( !candidate->m_absorbed &&
1161  candidate->m_strong_driver &&
1162  candidate != subgraph );
1163  } );
1164 
1165  // This is a list of connections on the current subgraph to compare to the
1166  // drivers of each candidate subgraph. If the current subgraph is a bus,
1167  // we should consider each bus member.
1168  std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
1169 
1170  // Also check the main driving connection
1171  connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
1172 
1173  auto add_connections_to_check = [&] ( CONNECTION_SUBGRAPH* aSubgraph ) {
1174  for( SCH_ITEM* possible_driver : aSubgraph->m_items )
1175  {
1176  if( possible_driver == aSubgraph->m_driver )
1177  continue;
1178 
1179  auto c = getDefaultConnection( possible_driver, aSubgraph );
1180 
1181  if( c )
1182  {
1183  if( c->Type() != aSubgraph->m_driver_connection->Type() )
1184  continue;
1185 
1186  if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
1187  continue;
1188 
1189  connections_to_check.push_back( c );
1190  wxLogTrace( ConnTrace,
1191  "%lu (%s): Adding secondary driver %s", aSubgraph->m_code,
1192  aSubgraph->m_driver_connection->Name( true ), c->Name( true ) );
1193  }
1194  }
1195  };
1196 
1197  // Now add other strong drivers
1198  // The actual connection attached to these items will have been overwritten
1199  // by the chosen driver of the subgraph, so we need to create a dummy connection
1200  add_connections_to_check( subgraph );
1201 
1202  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1203  {
1204  auto member = connections_to_check[i];
1205 
1206  if( member->IsBus() )
1207  {
1208  connections_to_check.insert( connections_to_check.end(),
1209  member->Members().begin(),
1210  member->Members().end() );
1211  }
1212 
1213  wxString test_name = member->Name( true );
1214 
1215  for( auto candidate : candidate_subgraphs )
1216  {
1217  if( candidate->m_absorbed )
1218  continue;
1219 
1220  bool match = false;
1221 
1222  if( candidate->m_driver_connection->Name( true ) == test_name )
1223  {
1224  match = true;
1225  }
1226  else
1227  {
1228  if( !candidate->m_multiple_drivers )
1229  continue;
1230 
1231  for( SCH_ITEM *driver : candidate->m_drivers )
1232  {
1233  if( driver == candidate->m_driver )
1234  continue;
1235 
1236  // Sheet pins are not candidates for merging
1237  if( driver->Type() == SCH_SHEET_PIN_T )
1238  continue;
1239 
1240  if( driver->Type() == SCH_PIN_T )
1241  {
1242  auto pin = static_cast<SCH_PIN*>( driver );
1243 
1244  if( pin->IsPowerConnection() && pin->GetShownName() == test_name )
1245  {
1246  match = true;
1247  break;
1248  }
1249  }
1250  else
1251  {
1252  wxASSERT( driver->Type() == SCH_LABEL_T ||
1253  driver->Type() == SCH_GLOBAL_LABEL_T ||
1254  driver->Type() == SCH_HIER_LABEL_T );
1255 
1256  if( subgraph->GetNameForDriver( driver ) == test_name )
1257  {
1258  match = true;
1259  break;
1260  }
1261  }
1262  }
1263  }
1264 
1265  if( match )
1266  {
1267  if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
1268  {
1269  wxLogTrace( ConnTrace, "%lu (%s) has bus child %lu (%s)", subgraph->m_code,
1270  connection->Name(), candidate->m_code, member->Name() );
1271 
1272  subgraph->m_bus_neighbors[member].insert( candidate );
1273  candidate->m_bus_parents[member].insert( subgraph );
1274  }
1275  else
1276  {
1277  wxLogTrace( ConnTrace, "%lu (%s) absorbs neighbor %lu (%s)",
1278  subgraph->m_code, connection->Name(),
1279  candidate->m_code, candidate->m_driver_connection->Name() );
1280 
1281  // Candidate may have other non-chosen drivers we need to follow
1282  add_connections_to_check( candidate );
1283 
1284  subgraph->Absorb( candidate );
1285  invalidated_subgraphs.insert( subgraph );
1286  }
1287  }
1288  }
1289  }
1290  }
1291 
1292  // Update any subgraph that was invalidated above
1293  for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
1294  {
1295  if( subgraph->m_absorbed )
1296  continue;
1297 
1298  subgraph->ResolveDrivers();
1299 
1300  if( subgraph->m_driver_connection->IsBus() )
1301  assignNetCodesToBus( subgraph->m_driver_connection );
1302  else
1303  assignNewNetCode( *subgraph->m_driver_connection );
1304 
1305  wxLogTrace( ConnTrace, "Re-resolving drivers for %lu (%s)", subgraph->m_code,
1306  subgraph->m_driver_connection->Name() );
1307  }
1308 
1309  // Absorbed subgraphs should no longer be considered
1310  m_driver_subgraphs.erase( std::remove_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1311  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1312  {
1313  return candidate->m_absorbed;
1314  } ),
1315  m_driver_subgraphs.end() );
1316 
1317  // Store global subgraphs for later reference
1318  std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
1319  std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1320  std::back_inserter( global_subgraphs ),
1321  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1322  {
1323  return !candidate->m_local_driver;
1324  } );
1325 
1326  // Recache remaining valid subgraphs by sheet path
1327  m_sheet_to_subgraphs_map.clear();
1328 
1329  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1330  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1331 
1332  // Update item connections at this point so that neighbor propagation works
1333  nextSubgraph.store( 0 );
1334 
1335  auto preliminaryUpdateTask =
1336  [&]() -> size_t
1337  {
1338  for( size_t subgraphId = nextSubgraph++;
1339  subgraphId < m_driver_subgraphs.size();
1340  subgraphId = nextSubgraph++ )
1341  {
1342  m_driver_subgraphs[subgraphId]->UpdateItemConnections();
1343  }
1344 
1345  return 1;
1346  };
1347 
1348  if( parallelThreadCount == 1 )
1349  preliminaryUpdateTask();
1350  else
1351  {
1352  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
1353  returns[ii] = std::async( std::launch::async, preliminaryUpdateTask );
1354 
1355  // Finalize the threads
1356  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
1357  returns[ii].wait();
1358  }
1359 
1360  // Next time through the subgraphs, we do some post-processing to handle things like
1361  // connecting bus members to their neighboring subgraphs, and then propagate connections
1362  // through the hierarchy
1363 
1364  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1365  {
1366  if( !subgraph->m_dirty )
1367  continue;
1368 
1369  wxLogTrace( ConnTrace, "Processing %lu (%s) for propagation", subgraph->m_code,
1370  subgraph->m_driver_connection->Name() );
1371 
1372  // For subgraphs that are driven by a global (power port or label) and have more
1373  // than one global driver, we need to seek out other subgraphs driven by the
1374  // same name as the non-chosen driver and update them to match the chosen one.
1375 
1376  if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
1377  {
1378  for( SCH_ITEM* driver : subgraph->m_drivers )
1379  {
1380  if( driver == subgraph->m_driver )
1381  continue;
1382 
1383  wxString secondary_name = subgraph->GetNameForDriver( driver );
1384 
1385  if( secondary_name == subgraph->m_driver_connection->Name() )
1386  continue;
1387 
1388  bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
1390 
1391  for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
1392  {
1393  if( candidate == subgraph )
1394  continue;
1395 
1396  if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
1397  continue;
1398 
1399  SCH_CONNECTION* conn = candidate->m_driver_connection;
1400 
1401  if( conn->Name() == secondary_name )
1402  {
1403  wxLogTrace( ConnTrace, "Global %lu (%s) promoted to %s", candidate->m_code,
1404  conn->Name(), subgraph->m_driver_connection->Name() );
1405 
1406  conn->Clone( *subgraph->m_driver_connection );
1407 
1408  candidate->m_dirty = false;
1409  }
1410  }
1411  }
1412  }
1413 
1414  // This call will handle descending the hierarchy and updating child subgraphs
1415  propagateToNeighbors( subgraph );
1416  }
1417 
1418  // Handle buses that have been linked together somewhere by member (net) connections.
1419  // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
1420 
1421  // For net subgraphs that have more than one bus parent, we need to ensure that those
1422  // buses are linked together in the final netlist. The final name of each bus might not
1423  // match the local name that was used to establish the parent-child relationship, because
1424  // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
1425  // we need to identify the appropriate bus members to link together (and their final names),
1426  // and then update all instances of the old name in the hierarchy.
1427 
1428  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1429  {
1430  // All SGs should have been processed by propagateToNeighbors above
1431  wxASSERT_MSG( !subgraph->m_dirty, "Subgraph not processed by propagateToNeighbors!" );
1432 
1433  if( subgraph->m_bus_parents.size() < 2 )
1434  continue;
1435 
1436  SCH_CONNECTION* conn = subgraph->m_driver_connection;
1437 
1438  wxLogTrace( ConnTrace, "%lu (%s) has multiple bus parents",
1439  subgraph->m_code, conn->Name() );
1440 
1441  wxASSERT( conn->IsNet() );
1442 
1443  for( const auto& ii : subgraph->m_bus_parents )
1444  {
1445  SCH_CONNECTION* link_member = ii.first.get();
1446 
1447  for( CONNECTION_SUBGRAPH* parent : ii.second )
1448  {
1449  while( parent->m_absorbed )
1450  parent = parent->m_absorbed_by;
1451 
1452  SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
1453 
1454  if( !match )
1455  {
1456  wxLogTrace( ConnTrace, "Warning: could not match %s inside %lu (%s)",
1457  conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
1458  continue;
1459  }
1460 
1461  if( conn->Name() != match->Name() )
1462  {
1463  wxString old_name = match->Name();
1464 
1465  wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", parent->m_code,
1466  parent->m_driver_connection->Name(), old_name, conn->Name() );
1467 
1468  match->Clone( *conn );
1469 
1470  auto jj = m_net_name_to_subgraphs_map.find( old_name );
1471 
1472  if( jj == m_net_name_to_subgraphs_map.end() )
1473  continue;
1474 
1475  for( CONNECTION_SUBGRAPH* old_sg : jj->second )
1476  {
1477  while( old_sg->m_absorbed )
1478  old_sg = old_sg->m_absorbed_by;
1479 
1480  old_sg->m_driver_connection->Clone( *conn );
1481  }
1482  }
1483  }
1484  }
1485  }
1486 
1487  nextSubgraph.store( 0 );
1488 
1489  auto updateItemConnectionsTask =
1490  [&]() -> size_t
1491  {
1492  for( size_t subgraphId = nextSubgraph++;
1493  subgraphId < m_driver_subgraphs.size();
1494  subgraphId = nextSubgraph++ )
1495  {
1496  CONNECTION_SUBGRAPH* subgraph = m_driver_subgraphs[subgraphId];
1497 
1498  // Make sure weakly-driven single-pin nets get the unconnected_ prefix
1499  if( !subgraph->m_strong_driver && subgraph->m_drivers.size() == 1 &&
1500  subgraph->m_driver->Type() == SCH_PIN_T )
1501  {
1502  SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
1503  wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
1504 
1506  }
1507 
1508  subgraph->m_dirty = false;
1509  subgraph->UpdateItemConnections();
1510 
1511  // No other processing to do on buses
1512  if( subgraph->m_driver_connection->IsBus() )
1513  continue;
1514 
1515  // As a visual aid, we can check sheet pins that are driven by themselves to see
1516  // if they should be promoted to buses
1517 
1518  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1519  {
1520  SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
1521 
1522  if( SCH_SHEET* sheet = pin->GetParent() )
1523  {
1524  wxString pinText = pin->GetText();
1525  SCH_SCREEN* screen = sheet->GetScreen();
1526 
1527  for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
1528  {
1529  SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
1530 
1531  if( label->GetText() == pinText )
1532  {
1533  SCH_SHEET_PATH path = subgraph->m_sheet;
1534  path.push_back( sheet );
1535 
1536  SCH_CONNECTION* parent_conn = label->Connection( &path );
1537 
1538  if( parent_conn && parent_conn->IsBus() )
1540 
1541  break;
1542  }
1543  }
1544 
1545  if( subgraph->m_driver_connection->IsBus() )
1546  continue;
1547  }
1548  }
1549  }
1550 
1551  return 1;
1552  };
1553 
1554  if( parallelThreadCount == 1 )
1555  updateItemConnectionsTask();
1556  else
1557  {
1558  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
1559  returns[ii] = std::async( std::launch::async, updateItemConnectionsTask );
1560 
1561  // Finalize the threads
1562  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
1563  returns[ii].wait();
1564  }
1565 
1568 
1569  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1570  {
1571  auto key = std::make_pair( subgraph->GetNetName(),
1572  subgraph->m_driver_connection->NetCode() );
1573  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1574 
1575  m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
1576  }
1577 }
1578 
1579 
1581 {
1582  int code;
1583 
1584  auto it = m_net_name_to_code_map.find( aConnection.Name() );
1585 
1586  if( it == m_net_name_to_code_map.end() )
1587  {
1588  code = m_last_net_code++;
1589  m_net_name_to_code_map[ aConnection.Name() ] = code;
1590  }
1591  else
1592  {
1593  code = it->second;
1594  }
1595 
1596  aConnection.SetNetCode( code );
1597 
1598  return code;
1599 }
1600 
1601 
1603 {
1604  auto connections_to_check( aConnection->Members() );
1605 
1606  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1607  {
1608  auto member = connections_to_check[i];
1609 
1610  if( member->IsBus() )
1611  {
1612  connections_to_check.insert( connections_to_check.end(),
1613  member->Members().begin(),
1614  member->Members().end() );
1615  continue;
1616  }
1617 
1618  assignNewNetCode( *member );
1619  }
1620 }
1621 
1622 
1624 {
1625  SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
1626  std::vector<CONNECTION_SUBGRAPH*> search_list;
1627  std::unordered_set<CONNECTION_SUBGRAPH*> visited;
1628  std::vector<SCH_CONNECTION*> stale_bus_members;
1629 
1630  auto visit =
1631  [&]( CONNECTION_SUBGRAPH* aParent )
1632  {
1633  for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
1634  {
1635  SCH_SHEET_PATH path = aParent->m_sheet;
1636  path.push_back( pin->GetParent() );
1637 
1638  auto it = m_sheet_to_subgraphs_map.find( path );
1639 
1640  if( it == m_sheet_to_subgraphs_map.end() )
1641  continue;
1642 
1643  for( CONNECTION_SUBGRAPH* candidate : it->second )
1644  {
1645  if( !candidate->m_strong_driver
1646  || candidate->m_hier_ports.empty()
1647  || visited.count( candidate ) )
1648  {
1649  continue;
1650  }
1651 
1652  for( SCH_HIERLABEL* label : candidate->m_hier_ports )
1653  {
1654  if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
1655  {
1656  wxLogTrace( ConnTrace, "%lu: found child %lu (%s)", aParent->m_code,
1657  candidate->m_code, candidate->m_driver_connection->Name() );
1658 
1659  candidate->m_hier_parent = aParent;
1660 
1661  search_list.push_back( candidate );
1662  break;
1663  }
1664  }
1665  }
1666  }
1667 
1668  for( SCH_HIERLABEL* label : aParent->m_hier_ports )
1669  {
1670  SCH_SHEET_PATH path = aParent->m_sheet;
1671  path.pop_back();
1672 
1673  auto it = m_sheet_to_subgraphs_map.find( path );
1674 
1675  if( it == m_sheet_to_subgraphs_map.end() )
1676  continue;
1677 
1678  for( CONNECTION_SUBGRAPH* candidate : it->second )
1679  {
1680  if( candidate->m_hier_pins.empty()
1681  || visited.count( candidate )
1682  || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
1683  {
1684  continue;
1685  }
1686 
1687  for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
1688  {
1689  SCH_SHEET_PATH pin_path = path;
1690  pin_path.push_back( pin->GetParent() );
1691 
1692  if( pin_path != aParent->m_sheet )
1693  continue;
1694 
1695  if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
1696  {
1697  wxLogTrace( ConnTrace, "%lu: found additional parent %lu (%s)",
1698  aParent->m_code, candidate->m_code,
1699  candidate->m_driver_connection->Name() );
1700 
1701  search_list.push_back( candidate );
1702  break;
1703  }
1704  }
1705  }
1706  }
1707  };
1708 
1709  auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
1710  for( const auto& kv : aParentGraph->m_bus_neighbors )
1711  {
1712  for( CONNECTION_SUBGRAPH* neighbor : kv.second )
1713  {
1714  // May have been absorbed but won't have been deleted
1715  while( neighbor->m_absorbed )
1716  neighbor = neighbor->m_absorbed_by;
1717 
1718  SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
1719 
1720  // Now member may be out of date, since we just cloned the
1721  // connection from higher up in the hierarchy. We need to
1722  // figure out what the actual new connection is.
1723  SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
1724 
1725  if( !member )
1726  {
1727  // Try harder: we might match on a secondary driver
1728  for( CONNECTION_SUBGRAPH* sg : kv.second )
1729  {
1730  if( sg->m_multiple_drivers )
1731  {
1732  SCH_SHEET_PATH sheet = sg->m_sheet;
1733 
1734  for( SCH_ITEM* driver : sg->m_drivers )
1735  {
1736  auto c = getDefaultConnection( driver, sg );
1737  member = matchBusMember( parent, c.get() );
1738 
1739  if( member )
1740  break;
1741  }
1742  }
1743 
1744  if( member )
1745  break;
1746  }
1747  }
1748 
1749  // This is bad, probably an ERC error
1750  if( !member )
1751  {
1752  wxLogTrace( ConnTrace, "Could not match bus member %s in %s",
1753  kv.first->Name(), parent->Name() );
1754  continue;
1755  }
1756 
1757  auto neighbor_conn = neighbor->m_driver_connection;
1758  auto neighbor_name = neighbor_conn->Name();
1759 
1760  // Matching name: no update needed
1761  if( neighbor_name == member->Name() )
1762  continue;
1763 
1764  // Was this neighbor already updated from a different sheet? Don't rename it again
1765  if( neighbor_conn->Sheet() != neighbor->m_sheet )
1766  continue;
1767 
1768  // Safety check against infinite recursion
1769  wxASSERT( neighbor_conn->IsNet() );
1770 
1771  wxLogTrace( ConnTrace, "%lu (%s) connected to bus member %s (local %s)",
1772  neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
1773 
1774  // Take whichever name is higher priority
1777  {
1778  member->Clone( *neighbor_conn );
1779  stale_bus_members.push_back( member );
1780  }
1781  else
1782  {
1783  neighbor_conn->Clone( *member );
1784 
1785  recacheSubgraphName( neighbor, neighbor_name );
1786 
1787  // Recurse onto this neighbor in case it needs to re-propagate
1788  neighbor->m_dirty = true;
1789  propagateToNeighbors( neighbor );
1790  }
1791  }
1792  }
1793  };
1794 
1795  // If we are a bus, we must propagate to local neighbors and then the hierarchy
1796  if( conn->IsBus() )
1797  propagate_bus_neighbors( aSubgraph );
1798 
1799  // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
1800  // If we only have one or the other, process (we can either go bottom-up or top-down depending
1801  // on which subgraph comes up first)
1802  if( !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
1803  {
1804  wxLogTrace( ConnTrace, "%lu (%s) has both hier ports and pins; deferring processing",
1805  aSubgraph->m_code, conn->Name() );
1806  return;
1807  }
1808  else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
1809  {
1810  wxLogTrace( ConnTrace, "%lu (%s) has no hier pins or ports; marking clean",
1811  aSubgraph->m_code, conn->Name() );
1812  aSubgraph->m_dirty = false;
1813  return;
1814  }
1815 
1816  visited.insert( aSubgraph );
1817 
1818  wxLogTrace( ConnTrace, "Propagating %lu (%s) to subsheets",
1819  aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
1820 
1821  visit( aSubgraph );
1822 
1823  for( unsigned i = 0; i < search_list.size(); i++ )
1824  {
1825  auto child = search_list[i];
1826 
1827  visited.insert( child );
1828 
1829  visit( child );
1830 
1831  child->m_dirty = false;
1832  }
1833 
1834  // Now, find the best driver for this chain of subgraphs
1835  CONNECTION_SUBGRAPH* original = aSubgraph;
1838  bool originalStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
1839  wxString originalName = original->m_driver_connection->Name();
1840 
1841  // Check if a subsheet has a higher-priority connection to the same net
1843  {
1844  for( CONNECTION_SUBGRAPH* subgraph : visited )
1845  {
1846  if( subgraph == original )
1847  continue;
1848 
1850  CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
1851 
1852  bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
1853  wxString candidateName = subgraph->m_driver_connection->Name();
1854  bool shorterPath = subgraph->m_sheet.size() < original->m_sheet.size();
1855  bool asGoodPath = subgraph->m_sheet.size() <= original->m_sheet.size();
1856 
1857  // Pick a better driving subgraph if it:
1858  // a) has a power pin or global driver
1859  // b) is a strong driver and we're a weak driver
1860  // c) is a higher priority strong driver
1861  // d) matches our priority, is a strong driver, and has a shorter path
1862  // e) matches our strength and is at least as short, and is alphabetically lower
1863 
1864  if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
1865  ( !originalStrong && candidateStrong ) ||
1866  ( priority > highest && candidateStrong ) ||
1867  ( priority == highest && candidateStrong && shorterPath ) ||
1868  ( ( originalStrong == candidateStrong ) && asGoodPath &&
1869  ( candidateName < originalName ) ) )
1870  {
1871  original = subgraph;
1872  highest = priority;
1873  originalStrong = candidateStrong;
1874  originalName = subgraph->m_driver_connection->Name();
1875  }
1876  }
1877  }
1878 
1879  if( original != aSubgraph )
1880  {
1881  wxLogTrace( ConnTrace, "%lu (%s) overridden by new driver %lu (%s)",
1882  aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), original->m_code,
1883  original->m_driver_connection->Name() );
1884  }
1885 
1886  conn = original->m_driver_connection;
1887 
1888  for( CONNECTION_SUBGRAPH* subgraph : visited )
1889  {
1890  wxString old_name = subgraph->m_driver_connection->Name();
1891 
1892  subgraph->m_driver_connection->Clone( *conn );
1893 
1894  if( old_name != conn->Name() )
1895  recacheSubgraphName( subgraph, old_name );
1896 
1897  if( conn->IsBus() )
1898  propagate_bus_neighbors( subgraph );
1899  }
1900 
1901  // Somewhere along the way, a bus member may have been upgraded to a global or power label.
1902  // Because this can happen anywhere, we need a second pass to update all instances of that bus
1903  // member to have the correct connection info
1904  if( conn->IsBus() && !stale_bus_members.empty() )
1905  {
1906  for( auto stale_member : stale_bus_members )
1907  {
1908  for( CONNECTION_SUBGRAPH* subgraph : visited )
1909  {
1910  SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
1911  stale_member );
1912 
1913  if( !member )
1914  {
1915  wxLogTrace( ConnTrace, "WARNING: failed to match stale member %s in %s.",
1916  stale_member->Name(), subgraph->m_driver_connection->Name() );
1917  continue;
1918  }
1919 
1920  wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", subgraph->m_code,
1921  subgraph->m_driver_connection->Name(), member->LocalName(),
1922  stale_member->Name() );
1923 
1924  member->Clone( *stale_member );
1925 
1926  propagate_bus_neighbors( subgraph );
1927  }
1928  }
1929  }
1930 
1931  aSubgraph->m_dirty = false;
1932 }
1933 
1934 
1935 std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
1936  CONNECTION_SUBGRAPH* aSubgraph )
1937 {
1938  auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
1939 
1940  switch( aItem->Type() )
1941  {
1942  case SCH_PIN_T:
1943  {
1944  auto pin = static_cast<SCH_PIN*>( aItem );
1945 
1946  if( pin->IsPowerConnection() )
1947  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1948 
1949  break;
1950  }
1951 
1952  case SCH_GLOBAL_LABEL_T:
1953  case SCH_HIER_LABEL_T:
1954  case SCH_LABEL_T:
1955  {
1956  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1957  break;
1958  }
1959 
1960  default:
1961  break;
1962  }
1963 
1964  if( c )
1965  {
1966  c->SetGraph( this );
1967  c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
1968  }
1969 
1970  return c;
1971 }
1972 
1973 
1975  SCH_CONNECTION* aSearch )
1976 {
1977  wxASSERT( aBusConnection->IsBus() );
1978 
1979  SCH_CONNECTION* match = nullptr;
1980 
1981  if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
1982  {
1983  // Vector bus: compare against index, because we allow the name
1984  // to be different
1985 
1986  for( const auto& bus_member : aBusConnection->Members() )
1987  {
1988  if( bus_member->VectorIndex() == aSearch->VectorIndex() )
1989  {
1990  match = bus_member.get();
1991  break;
1992  }
1993  }
1994  }
1995  else
1996  {
1997  // Group bus
1998  for( const auto& c : aBusConnection->Members() )
1999  {
2000  // Vector inside group: compare names, because for bus groups
2001  // we expect the naming to be consistent across all usages
2002  // TODO(JE) explain this in the docs
2003  if( c->Type() == CONNECTION_TYPE::BUS )
2004  {
2005  for( const auto& bus_member : c->Members() )
2006  {
2007  if( bus_member->LocalName() == aSearch->LocalName() )
2008  {
2009  match = bus_member.get();
2010  break;
2011  }
2012  }
2013  }
2014  else if( c->LocalName() == aSearch->LocalName() )
2015  {
2016  match = c.get();
2017  break;
2018  }
2019  }
2020  }
2021 
2022  return match;
2023 }
2024 
2025 
2027  const wxString& aOldName )
2028 {
2029  auto it = m_net_name_to_subgraphs_map.find( aOldName );
2030 
2031  if( it != m_net_name_to_subgraphs_map.end() )
2032  {
2033  std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
2034  vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
2035  }
2036 
2037  wxLogTrace( ConnTrace, "recacheSubgraphName: %s => %s", aOldName,
2038  aSubgraph->m_driver_connection->Name() );
2039 
2040  m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
2041 }
2042 
2043 
2044 std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
2045 {
2046  auto it = m_bus_alias_cache.find( aName );
2047 
2048  return it != m_bus_alias_cache.end() ? it->second : nullptr;
2049 }
2050 
2051 
2052 std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
2053 {
2054  std::vector<const CONNECTION_SUBGRAPH*> ret;
2055 
2056  for( auto&& subgraph : m_subgraphs )
2057  {
2058  // Graph is supposed to be up-to-date before calling this
2059  wxASSERT( !subgraph->m_dirty );
2060 
2061  if( !subgraph->m_driver )
2062  continue;
2063 
2064  auto sheet = subgraph->m_sheet;
2065  auto connection = subgraph->m_driver->Connection( &sheet );
2066 
2067  if( !connection->IsBus() )
2068  continue;
2069 
2070  auto labels = subgraph->GetBusLabels();
2071 
2072  if( labels.size() > 1 )
2073  {
2074  bool different = false;
2075  wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
2076 
2077  for( unsigned i = 1; i < labels.size(); ++i )
2078  {
2079  if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
2080  {
2081  different = true;
2082  break;
2083  }
2084  }
2085 
2086  if( !different )
2087  continue;
2088 
2089  wxLogTrace( ConnTrace, "SG %ld (%s) has multiple bus labels", subgraph->m_code,
2090  connection->Name() );
2091 
2092  ret.push_back( subgraph );
2093  }
2094  }
2095 
2096  return ret;
2097 }
2098 
2099 
2101  const SCH_SHEET_PATH& aPath )
2102 {
2103  auto it = m_net_name_to_subgraphs_map.find( aNetName );
2104 
2105  if( it == m_net_name_to_subgraphs_map.end() )
2106  return nullptr;
2107 
2108  for( CONNECTION_SUBGRAPH* sg : it->second )
2109  {
2110  // Cache is supposed to be valid by now
2111  wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
2112 
2113  if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
2114  return sg;
2115  }
2116 
2117  return nullptr;
2118 }
2119 
2120 
2122 {
2123  auto it = m_net_name_to_subgraphs_map.find( aNetName );
2124 
2125  if( it == m_net_name_to_subgraphs_map.end() )
2126  return nullptr;
2127 
2128  wxASSERT( !it->second.empty() );
2129 
2130  return it->second[0];
2131 }
2132 
2133 
2135 {
2136  auto it = m_item_to_subgraph_map.find( aItem );
2137  CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
2138 
2139  while( ret && ret->m_absorbed )
2140  ret = ret->m_absorbed_by;
2141 
2142  return ret;
2143 }
2144 
2145 
2147 {
2148  int error_count = 0;
2149 
2150  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::RunERC" );
2151 
2152  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2153 
2154  // We don't want to run many ERC checks more than once on a given screen even though it may
2155  // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
2156  std::set<SCH_ITEM*> seenDriverInstances;
2157 
2158  for( auto&& subgraph : m_subgraphs )
2159  {
2160  // Graph is supposed to be up-to-date before calling RunERC()
2161  wxASSERT( !subgraph->m_dirty );
2162 
2163  if( subgraph->m_absorbed )
2164  continue;
2165 
2166  if( seenDriverInstances.count( subgraph->m_driver ) )
2167  continue;
2168 
2169  if( subgraph->m_driver )
2170  seenDriverInstances.insert( subgraph->m_driver );
2171 
2182  if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
2183  {
2184  if( !ercCheckMultipleDrivers( subgraph ) )
2185  error_count++;
2186  }
2187 
2188  subgraph->ResolveDrivers( false );
2189 
2190  if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
2191  {
2192  if( !ercCheckBusToNetConflicts( subgraph ) )
2193  error_count++;
2194  }
2195 
2196  if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
2197  {
2198  if( !ercCheckBusToBusEntryConflicts( subgraph ) )
2199  error_count++;
2200  }
2201 
2202  if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
2203  {
2204  if( !ercCheckBusToBusConflicts( subgraph ) )
2205  error_count++;
2206  }
2207 
2208  if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
2209  {
2210  if( !ercCheckFloatingWires( subgraph ) )
2211  error_count++;
2212  }
2213 
2214  // The following checks are always performed since they don't currently
2215  // have an option exposed to the user
2216 
2217  if( !ercCheckNoConnects( subgraph ) )
2218  error_count++;
2219 
2220  if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
2221  || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
2222  {
2223  if( !ercCheckLabels( subgraph ) )
2224  error_count++;
2225  }
2226  }
2227 
2228  // Hierarchical sheet checking is done at the schematic level
2229  if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
2230  error_count += ercCheckHierSheets();
2231 
2232  return error_count;
2233 }
2234 
2235 
2237 {
2238  if( !aSubgraph->m_second_driver )
2239  return true;
2240 
2241  SCH_ITEM* primary = aSubgraph->m_first_driver;
2242  SCH_ITEM* secondary = aSubgraph->m_second_driver;
2243 
2244  wxPoint pos = primary->Type() == SCH_PIN_T ?
2245  static_cast<SCH_PIN*>( primary )->GetTransformedPosition() :
2246  primary->GetPosition();
2247 
2248  wxString primaryName = aSubgraph->GetNameForDriver( primary );
2249  wxString secondaryName = aSubgraph->GetNameForDriver( secondary );
2250 
2251  wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
2252  "items; %s will be used in the netlist" ),
2253  primaryName, secondaryName, primaryName );
2254 
2255  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
2256  ercItem->SetItems( primary, secondary );
2257  ercItem->SetErrorMessage( msg );
2258 
2259  SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
2260  aSubgraph->m_sheet.LastScreen()->Append( marker );
2261 
2262  return false;
2263 }
2264 
2265 
2267 {
2268  auto sheet = aSubgraph->m_sheet;
2269  auto screen = sheet.LastScreen();
2270 
2271  SCH_ITEM* net_item = nullptr;
2272  SCH_ITEM* bus_item = nullptr;
2273  SCH_CONNECTION conn( this );
2274 
2275  for( SCH_ITEM* item : aSubgraph->m_items )
2276  {
2277  switch( item->Type() )
2278  {
2279  case SCH_LINE_T:
2280  {
2281  if( item->GetLayer() == LAYER_BUS )
2282  bus_item = ( !bus_item ) ? item : bus_item;
2283  else
2284  net_item = ( !net_item ) ? item : net_item;
2285  break;
2286  }
2287 
2288  case SCH_LABEL_T:
2289  case SCH_GLOBAL_LABEL_T:
2290  case SCH_SHEET_PIN_T:
2291  case SCH_HIER_LABEL_T:
2292  {
2293  SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2294  conn.ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
2295 
2296  if( conn.IsBus() )
2297  bus_item = ( !bus_item ) ? item : bus_item;
2298  else
2299  net_item = ( !net_item ) ? item : net_item;
2300  break;
2301  }
2302 
2303  default:
2304  break;
2305  }
2306  }
2307 
2308  if( net_item && bus_item )
2309  {
2310  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
2311  ercItem->SetItems( net_item, bus_item );
2312 
2313  SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
2314  screen->Append( marker );
2315 
2316  return false;
2317  }
2318 
2319  return true;
2320 }
2321 
2322 
2324 {
2325  wxString msg;
2326  auto sheet = aSubgraph->m_sheet;
2327  auto screen = sheet.LastScreen();
2328 
2329  SCH_ITEM* label = nullptr;
2330  SCH_ITEM* port = nullptr;
2331 
2332  for( auto item : aSubgraph->m_items )
2333  {
2334  switch( item->Type() )
2335  {
2336  case SCH_TEXT_T:
2337  case SCH_GLOBAL_LABEL_T:
2338  {
2339  if( !label && item->Connection( &sheet )->IsBus() )
2340  label = item;
2341  break;
2342  }
2343 
2344  case SCH_SHEET_PIN_T:
2345  case SCH_HIER_LABEL_T:
2346  {
2347  if( !port && item->Connection( &sheet )->IsBus() )
2348  port = item;
2349  break;
2350  }
2351 
2352  default:
2353  break;
2354  }
2355  }
2356 
2357  if( label && port )
2358  {
2359  bool match = false;
2360 
2361  for( const auto& member : label->Connection( &sheet )->Members() )
2362  {
2363  for( const auto& test : port->Connection( &sheet )->Members() )
2364  {
2365  if( test != member && member->Name() == test->Name() )
2366  {
2367  match = true;
2368  break;
2369  }
2370  }
2371 
2372  if( match )
2373  break;
2374  }
2375 
2376  if( !match )
2377  {
2378  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
2379  ercItem->SetItems( label, port );
2380 
2381  SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
2382  screen->Append( marker );
2383 
2384  return false;
2385  }
2386  }
2387 
2388  return true;
2389 }
2390 
2391 
2393 {
2394  bool conflict = false;
2395  auto sheet = aSubgraph->m_sheet;
2396  auto screen = sheet.LastScreen();
2397 
2398  SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2399  SCH_ITEM* bus_wire = nullptr;
2400  wxString bus_name;
2401 
2402  if( !aSubgraph->m_driver_connection )
2403  {
2404  // Incomplete bus entry. Let the unconnected tests handle it.
2405  return true;
2406  }
2407 
2408  for( auto item : aSubgraph->m_items )
2409  {
2410  switch( item->Type() )
2411  {
2412  case SCH_BUS_WIRE_ENTRY_T:
2413  {
2414  if( !bus_entry )
2415  bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2416  break;
2417  }
2418 
2419  default:
2420  break;
2421  }
2422  }
2423 
2424  if( bus_entry && bus_entry->m_connected_bus_item )
2425  {
2426  bus_wire = bus_entry->m_connected_bus_item;
2427 
2428  wxASSERT( bus_wire->Type() == SCH_LINE_T );
2429 
2430  // In some cases, the connection list (SCH_CONNECTION*) can be null.
2431  // Skip null connections.
2432  if( bus_entry->Connection( &sheet )
2433  && bus_wire->Type() == SCH_LINE_T
2434  && bus_wire->Connection( &sheet ) )
2435  {
2436  conflict = true; // Assume a conflict; we'll reset if we find it's OK
2437 
2438  bus_name = bus_wire->Connection( &sheet )->Name();
2439 
2440  wxString test_name = bus_entry->Connection( &sheet )->Name();
2441 
2442  for( const auto& member : bus_wire->Connection( &sheet )->Members() )
2443  {
2444  if( member->Type() == CONNECTION_TYPE::BUS )
2445  {
2446  for( const auto& sub_member : member->Members() )
2447  {
2448  if( sub_member->Name() == test_name )
2449  conflict = false;
2450  }
2451  }
2452  else if( member->Name() == test_name )
2453  {
2454  conflict = false;
2455  }
2456  }
2457  }
2458  }
2459 
2460  // Don't report warnings if this bus member has been overridden by a higher priority power pin
2461  // or global label
2462  if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
2464  conflict = false;
2465 
2466  if( conflict )
2467  {
2468  wxString netName = aSubgraph->m_driver_connection->Name();
2469  wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
2470  " member of that bus" ),
2471  UnescapeString( netName ),
2472  UnescapeString( bus_name ) );
2473  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
2474  ercItem->SetItems( bus_entry, bus_wire );
2475  ercItem->SetErrorMessage( msg );
2476 
2477  SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
2478  screen->Append( marker );
2479 
2480  return false;
2481  }
2482 
2483  return true;
2484 }
2485 
2486 
2487 // TODO(JE) Check sheet pins here too?
2489 {
2490  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2491  wxString msg;
2492  const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2493  SCH_SCREEN* screen = sheet.LastScreen();
2494  bool ok = true;
2495 
2496  if( aSubgraph->m_no_connect != nullptr )
2497  {
2498  bool has_invalid_items = false;
2499  bool has_other_items = false;
2500  SCH_PIN* pin = nullptr;
2501  std::vector<SCH_ITEM*> invalid_items;
2502  wxPoint noConnectPos = aSubgraph->m_no_connect->GetPosition();
2503  double minDist = 0;
2504 
2505  // Any subgraph that contains both a pin and a no-connect should not
2506  // contain any other driving items.
2507 
2508  for( auto item : aSubgraph->m_items )
2509  {
2510  switch( item->Type() )
2511  {
2512  case SCH_PIN_T:
2513  {
2514  SCH_PIN* candidate = static_cast<SCH_PIN*>( item );
2515  double dist = VECTOR2I( candidate->GetTransformedPosition() - noConnectPos )
2516  .SquaredEuclideanNorm();
2517 
2518  if( !pin || dist < minDist )
2519  {
2520  pin = candidate;
2521  minDist = dist;
2522  }
2523 
2524  has_invalid_items |= has_other_items;
2525  has_other_items = true;
2526  break;
2527  }
2528 
2529  case SCH_LINE_T:
2530  case SCH_JUNCTION_T:
2531  case SCH_NO_CONNECT_T:
2532  break;
2533 
2534  default:
2535  has_invalid_items = true;
2536  has_other_items = true;
2537  invalid_items.push_back( item );
2538  }
2539  }
2540 
2541  if( pin && has_invalid_items && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
2542  {
2543  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
2544  ercItem->SetItems( pin );
2545 
2546  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2547  screen->Append( marker );
2548 
2549  ok = false;
2550  }
2551 
2552  if( !has_other_items && settings.IsTestEnabled(ERCE_NOCONNECT_NOT_CONNECTED ) )
2553  {
2554  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
2555  ercItem->SetItems( aSubgraph->m_no_connect );
2556 
2557  SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
2558  screen->Append( marker );
2559 
2560  ok = false;
2561  }
2562  }
2563  else
2564  {
2565  bool has_other_connections = false;
2566  std::vector<SCH_PIN*> pins;
2567 
2568  // Any subgraph that lacks a no-connect and contains a pin should also
2569  // contain at least one other potential driver
2570 
2571  for( SCH_ITEM* item : aSubgraph->m_items )
2572  {
2573  switch( item->Type() )
2574  {
2575  case SCH_PIN_T:
2576  {
2577  if( !pins.empty() )
2578  has_other_connections = true;
2579 
2580  pins.emplace_back( static_cast<SCH_PIN*>( item ) );
2581 
2582  break;
2583  }
2584 
2585  default:
2586  if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
2587  has_other_connections = true;
2588 
2589  break;
2590  }
2591  }
2592 
2593  // For many checks, we can just use the first pin
2594  SCH_PIN* pin = pins.empty() ? nullptr : pins[0];
2595 
2596  // Check if invisible power input pins connect to anything else via net name,
2597  // but not for power symbols as the ones in the standard library all have invisible pins
2598  // and we want to throw unconnected errors for those even if they are connected to other
2599  // net items by name, because usually failing to connect them graphically is a mistake
2600  if( pin && !has_other_connections
2601  && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
2602  && !pin->IsVisible()
2603  && !pin->GetLibPin()->GetParent()->IsPower() )
2604  {
2605  wxString name = pin->Connection( &sheet )->Name();
2606  wxString local_name = pin->Connection( &sheet )->Name( true );
2607 
2608  if( m_global_label_cache.count( name ) ||
2609  ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
2610  {
2611  has_other_connections = true;
2612  }
2613  }
2614 
2615  // Only one pin, and it's not a no-connect pin
2616  if( pin && !has_other_connections
2617  && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
2618  && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
2619  && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2620  {
2621  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2622  ercItem->SetItems( pin );
2623 
2624  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2625  screen->Append( marker );
2626 
2627  ok = false;
2628  }
2629 
2630  // If there are multiple pins in this SG, they might be indirectly connected (by netname)
2631  // rather than directly connected (by wires). We want to flag dangling pins even if they
2632  // join nets with another pin, as it's often a mistake
2633  if( pins.size() > 1 )
2634  {
2635  for( SCH_PIN* testPin : pins )
2636  {
2637  // We only apply this test to power symbols, because other symbols have invisible
2638  // pins that are meant to be dangling, but the KiCad standard library power symbols
2639  // have invisible pins that are *not* meant to be dangling.
2640  if( testPin->GetLibPin()->GetParent()->IsPower()
2641  && testPin->ConnectedItems( sheet ).empty()
2642  && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2643  {
2644  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2645  ercItem->SetItems( testPin );
2646 
2647  SCH_MARKER* marker = new SCH_MARKER( ercItem,
2648  testPin->GetTransformedPosition() );
2649  screen->Append( marker );
2650 
2651  ok = false;
2652  }
2653  }
2654  }
2655  }
2656 
2657  return ok;
2658 }
2659 
2660 
2662 {
2663  if( aSubgraph->m_driver )
2664  return true;
2665 
2666  std::vector<SCH_ITEM*> wires;
2667 
2668  // We've gotten this far, so we know we have no valid driver. All we need to do is check
2669  // for a wire that we can place the error on.
2670 
2671  for( SCH_ITEM* item : aSubgraph->m_items )
2672  {
2673  if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
2674  wires.emplace_back( item );
2675  else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
2676  wires.emplace_back( item );
2677  }
2678 
2679  if( !wires.empty() )
2680  {
2681  SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
2682 
2683  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
2684  ercItem->SetItems( wires[0],
2685  wires.size() > 1 ? wires[1] : nullptr,
2686  wires.size() > 2 ? wires[2] : nullptr,
2687  wires.size() > 3 ? wires[3] : nullptr );
2688 
2689  SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
2690  screen->Append( marker );
2691 
2692  return false;
2693  }
2694 
2695  return true;
2696 }
2697 
2698 
2700 {
2701  // Label connection rules:
2702  // Local labels are flagged if they don't connect to any pins and don't have a no-connect
2703  // Global labels are flagged if they appear only once, don't connect to any local labels,
2704  // and don't have a no-connect marker
2705 
2706  // So, if there is a no-connect, we will never generate a warning here
2707  if( aSubgraph->m_no_connect )
2708  return true;
2709 
2710  if( !aSubgraph->m_driver_connection )
2711  return true;
2712 
2713  // Buses are excluded from this test: many users create buses with only a single instance
2714  // and it's not really a problem as long as the nets in the bus pass ERC
2715  if( aSubgraph->m_driver_connection->IsBus() )
2716  return true;
2717 
2718  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2719  bool ok = true;
2720  SCH_TEXT* text = nullptr;
2721  bool hasOtherConnections = false;
2722  int pinCount = 0;
2723 
2724  for( auto item : aSubgraph->m_items )
2725  {
2726  switch( item->Type() )
2727  {
2728  case SCH_LABEL_T:
2729  case SCH_GLOBAL_LABEL_T:
2730  case SCH_HIER_LABEL_T:
2731  {
2732  text = static_cast<SCH_TEXT*>( item );
2733 
2734  // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
2735  // we want to error if an individual label in the subgraph is floating, even if it's
2736  // connected to other valid things by way of another label on the same sheet.
2737 
2738  if( text->IsDangling() && settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
2739  {
2740  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
2741  ercItem->SetItems( text );
2742 
2743  SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
2744  aSubgraph->m_sheet.LastScreen()->Append( marker );
2745  ok = false;
2746  }
2747 
2748  break;
2749  }
2750 
2751  case SCH_PIN_T:
2752  case SCH_SHEET_PIN_T:
2753  hasOtherConnections = true;
2754  pinCount++;
2755  break;
2756 
2757  default:
2758  break;
2759  }
2760  }
2761 
2762  if( !text )
2763  return true;
2764 
2765  bool isGlobal = text->Type() == SCH_GLOBAL_LABEL_T;
2766  int errCode = isGlobal ? ERCE_GLOBLABEL : ERCE_LABEL_NOT_CONNECTED;
2767 
2768  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2769 
2770  wxString name = EscapeString( text->GetShownText(), CTX_NETNAME );
2771 
2772  if( isGlobal )
2773  {
2774  // This will be set to true if the global is connected to a pin above, but we
2775  // want to reset this to false so that globals get flagged if they only have a
2776  // single instance connected to a single pin
2777  hasOtherConnections = ( pinCount > 1 );
2778 
2779  auto it = m_net_name_to_subgraphs_map.find( name );
2780 
2781  if( it != m_net_name_to_subgraphs_map.end() )
2782  {
2783  if( it->second.size() > 1 || aSubgraph->m_multiple_drivers )
2784  hasOtherConnections = true;
2785  }
2786  }
2787  else if( text->Type() == SCH_HIER_LABEL_T )
2788  {
2789  // For a hier label, check if the parent pin is connected
2790  if( aSubgraph->m_hier_parent &&
2791  ( aSubgraph->m_hier_parent->m_strong_driver ||
2792  aSubgraph->m_hier_parent->m_drivers.size() > 1) )
2793  {
2794  // For now, a simple check: if there is more than one driver, the parent is probably
2795  // connected elsewhere (because at least one driver will be the hier pin itself)
2796  hasOtherConnections = true;
2797  }
2798  }
2799  else
2800  {
2801  auto pair = std::make_pair( aSubgraph->m_sheet, name );
2802  auto it = m_local_label_cache.find( pair );
2803 
2804  if( it != m_local_label_cache.end() && it->second.size() > 1 )
2805  hasOtherConnections = true;
2806  }
2807 
2808  if( !hasOtherConnections && settings.IsTestEnabled( errCode ) )
2809  {
2810  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
2811  ercItem->SetItems( text );
2812 
2813  SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
2814  aSubgraph->m_sheet.LastScreen()->Append( marker );
2815 
2816  return false;
2817  }
2818 
2819  return ok;
2820 }
2821 
2822 
2824 {
2825  int errors = 0;
2826 
2827  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2828 
2829  for( const SCH_SHEET_PATH& sheet : m_sheetList )
2830  {
2831  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
2832  {
2833  if( item->Type() != SCH_SHEET_T )
2834  continue;
2835 
2836  SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
2837 
2838  std::map<wxString, SCH_SHEET_PIN*> pins;
2839  std::map<wxString, SCH_HIERLABEL*> labels;
2840 
2841  for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
2842  {
2843  pins[pin->GetText()] = pin;
2844 
2845  if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2846  {
2847  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2848  ercItem->SetItems( pin );
2849 
2850  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
2851  sheet.LastScreen()->Append( marker );
2852 
2853  errors++;
2854  }
2855  }
2856 
2857  std::set<wxString> matchedPins;
2858 
2859  for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
2860  {
2861  if( subItem->Type() == SCH_HIER_LABEL_T )
2862  {
2863  SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
2864 
2865  if( !pins.count( label->GetText() ) )
2866  labels[label->GetText()] = label;
2867  else
2868  matchedPins.insert( label->GetText() );
2869  }
2870  }
2871 
2872  for( const wxString& matched : matchedPins )
2873  pins.erase( matched );
2874 
2875  for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
2876  {
2877  wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
2878  "label inside the sheet" ),
2879  UnescapeString( unmatched.first ) );
2880 
2881  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
2882  ercItem->SetItems( unmatched.second );
2883  ercItem->SetErrorMessage( msg );
2884  ercItem->SetIsSheetSpecific();
2885 
2886  SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
2887  sheet.LastScreen()->Append( marker );
2888 
2889  errors++;
2890  }
2891 
2892  for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
2893  {
2894  wxString msg = wxString::Format( _( "Hierarchical label %s has no matching "
2895  "sheet pin in the parent sheet" ),
2896  UnescapeString( unmatched.first ) );
2897 
2898  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
2899  ercItem->SetItems( unmatched.second );
2900  ercItem->SetErrorMessage( msg );
2901  ercItem->SetIsSheetSpecific();
2902 
2903  SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
2904  parentSheet->GetScreen()->Append( marker );
2905 
2906  errors++;
2907  }
2908  }
2909  }
2910 
2911  return errors;
2912 }
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:85
power input (GND, VCC for ICs). Must be connected to a power output.
bool ercCheckMultipleDrivers(const CONNECTION_SUBGRAPH *aSubgraph)
If the subgraph has multiple drivers of equal priority that are graphically connected,...
SCH_SHEET_PATH m_sheet
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:216
void SetBusCode(int aCode)
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
Pin not connected and not no connect symbol.
Definition: erc_settings.h:41
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.
void Clone(const SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
A connection between bus objects doesn't share at least one net.
Definition: erc_settings.h:59
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
#define kv
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:146
void Absorb(CONNECTION_SUBGRAPH *aOther)
Combines another subgraph on the same sheet into this one.
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:194
This item represents a bus group.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
SCH_ITEM * m_second_driver
Used for multiple drivers ERC message; stores the second possible driver (or nullptr)
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
Return true if this item should propagate connection info to aItem.
Definition: sch_item.h:422
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:123
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
A bus wire is graphically connected to a net port/pin (or vice versa).
Definition: erc_settings.h:61
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Return the priority (higher is more important) of a candidate driver.
bool ercCheckFloatingWires(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for floating wires.
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)
Some wires are not connected to anything else.
Definition: erc_settings.h:65
void SetName(const wxString &aName)
NET_MAP m_net_code_to_subgraphs_map
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr)
Updates the connection graph for the given list of sheets.
std::vector< SCH_ITEM * > m_items
Label not connected to anything.
Definition: erc_settings.h:49
SCHEMATIC * m_schematic
The schematic this graph represents.
virtual wxPoint GetPosition() const
Definition: eda_item.h:252
wxString LocalName() const
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:103
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.
void updateItemConnectivity(const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
Updates the graphical connectivity between items (i.e.
int ercCheckHierSheets()
Checks that a hierarchical sheet has at least one matching label inside the sheet for each port on th...
static const wxChar ConnTrace[]
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:131
not internally connected (may be connected to anything)
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
wxString Name(bool aIgnoreSheet=false) const
const wxString & GetNameForDriver(SCH_ITEM *aItem)
Returns the candidate net name for a driver.
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)
SCH_LINE * GetBus(const wxPoint &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T) const
Definition: sch_screen.h:396
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:279
bool ResolveDrivers(bool aCheckMultipleDrivers=false)
Determines which potential driver should drive the subgraph.
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.
#define _(s)
bool m_strong_driver
True if the driver is "strong": a label or power object.
std::vector< SCH_ITEM * > m_drivers
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:65
Container for ERC settings.
Definition: erc_settings.h:106
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
long VectorIndex() const
A wire connected to a bus doesn't match the bus.
Definition: erc_settings.h:57
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
A no connect symbol is not connected to anything.
Definition: erc_settings.h:48
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.
This item represents a net.
SCH_ITEM * m_first_driver
Stores the primary driver for the multiple drivers ERC check.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
static const wxChar ConnProfileMask[]
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:272
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:184
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static bool m_allowRealTime
A global label is unique.
Definition: erc_settings.h:63
A no connect symbol is connected to more than 1 pin.
Definition: erc_settings.h:47
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:87
bool IsNet() const
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
Schematic symbol object.
Definition: sch_symbol.h:78
SCH_SCREEN * LastScreen()
const char * name
Definition: DXF_plotter.cpp:59
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
Mismatch between hierarchical labels and pins sheets.
Definition: erc_settings.h:46
SCH_ITEM_SET & ConnectedItems(const SCH_SHEET_PATH &aPath)
Retrieve the set of items connected to this item on the given sheet.
Definition: sch_item.cpp:164
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:144
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
SCH_SHEET_LIST m_sheetList
std::shared_ptr< SCH_CONNECTION > getDefaultConnection(SCH_ITEM *aItem, CONNECTION_SUBGRAPH *aSubgraph)
Builds a new default connection for the given item based on its properties.
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:214
std::vector< SCH_ITEM * > GetBusLabels() const
Returns all the bus labels attached to this subgraph (if any)
virtual wxString GetSelectMenuText(EDA_UNITS aUnits) const
Return the text to display to be used in the selection clarification context menu when multiple items...
Definition: eda_item.cpp:109
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
Definition: string.cpp:141
EE_RTREE & Items()
Definition: sch_screen.h:102
std::map< wxString, int > m_bus_name_to_code_map
The common library.
wxString driverName(SCH_ITEM *aItem) const
std::map< wxString, int > m_net_name_to_code_map
CONNECTION_SUBGRAPH * m_hier_parent
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.
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition: profile.h:102
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem)
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:136
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)
Conflicting drivers (labels, etc) on a subgraph.
Definition: erc_settings.h:56
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:197
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:133
int RunERC()
Runs electrical rule checks on the connectivity graph.
void SetNetCode(int aCode)
wxString GetNetName() const
Returns the fully-qualified net name for this subgraph (if one exists)
PRIORITY GetDriverPriority()
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve a list of the SCH_PINs for the given sheet path.
Definition: sch_symbol.cpp:856
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.
PINSHEETLABEL_SHAPE GetShape() const
Definition: sch_text.h:161
bool ercCheckBusToBusConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting connections between two bus items.
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
std::unordered_map< SCH_ITEM *, wxString > m_driver_name_cache
A cache of escaped netnames from schematic items.
wxPoint GetPosition() const override
wxPoint GetTransformedPosition() const
Definition: sch_pin.cpp:297
void UpdateItemConnections()
Updates all items to match the driver connection.