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