KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Jon Evans <[email protected]>
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 <algorithm>
24#include <list>
25#include <future>
26#include <vector>
27#include <unordered_map>
28#include <app_monitor.h>
29#include <core/profile.h>
30#include <core/kicad_algo.h>
31#include <common.h>
32#include <erc/erc.h>
33#include <pin_type.h>
34#include <sch_bus_entry.h>
35#include <sch_symbol.h>
36#include <sch_edit_frame.h>
37#include <sch_line.h>
38#include <sch_marker.h>
39#include <sch_pin.h>
40#include <sch_rule_area.h>
41#include <sch_sheet.h>
42#include <sch_sheet_path.h>
43#include <sch_sheet_pin.h>
44#include <sch_text.h>
45#include <schematic.h>
46#include <symbol.h>
47#include <connection_graph.h>
50#include <widgets/ui_common.h>
51#include <string_utils.h>
52#include <thread_pool.h>
53#include <wx/log.h>
54
55#include <advanced_config.h> // for realtime connectivity switch in release builds
56
57
62static const wxChar DanglingProfileMask[] = wxT( "CONN_PROFILE" );
63
64
69static const wxChar ConnTrace[] = wxT( "CONN" );
70
71
73{
74 m_items.erase( aItem );
75 m_drivers.erase( aItem );
76
77 if( aItem == m_driver )
78 {
79 m_driver = nullptr;
80 m_driver_connection = nullptr;
81 }
82
83 if( aItem->Type() == SCH_SHEET_PIN_T )
84 m_hier_pins.erase( static_cast<SCH_SHEET_PIN*>( aItem ) );
85
86 if( aItem->Type() == SCH_HIER_LABEL_T )
87 m_hier_ports.erase( static_cast<SCH_HIERLABEL*>( aItem ) );
88}
89
90
92{
93 m_items.erase( aOldItem );
94 m_items.insert( aNewItem );
95
96 m_drivers.erase( aOldItem );
97 m_drivers.insert( aNewItem );
98
99 if( aOldItem == m_driver )
100 {
101 m_driver = aNewItem;
103 }
104
105 SCH_CONNECTION* old_conn = aOldItem->Connection( &m_sheet );
106 SCH_CONNECTION* new_conn = aNewItem->GetOrInitConnection( m_sheet, m_graph );
107
108 if( old_conn && new_conn )
109 {
110 new_conn->Clone( *old_conn );
111
112 if( old_conn->IsDriver() )
113 new_conn->SetDriver( aNewItem );
114
115 new_conn->ClearDirty();
116 }
117
118 if( aOldItem->Type() == SCH_SHEET_PIN_T )
119 {
120 m_hier_pins.erase( static_cast<SCH_SHEET_PIN*>( aOldItem ) );
121 m_hier_pins.insert( static_cast<SCH_SHEET_PIN*>( aNewItem ) );
122 }
123
124 if( aOldItem->Type() == SCH_HIER_LABEL_T )
125 {
126 m_hier_ports.erase( static_cast<SCH_HIERLABEL*>( aOldItem ) );
127 m_hier_ports.insert( static_cast<SCH_HIERLABEL*>( aNewItem ) );
128 }
129}
130
131
132bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers )
133{
134 std::lock_guard lock( m_driver_mutex );
135
136 // Collect candidate drivers of highest priority in a simple vector which will be
137 // sorted later. Using a vector makes the ranking logic explicit and easier to
138 // maintain than relying on the ordering semantics of std::set.
139 PRIORITY highest_priority = PRIORITY::INVALID;
140 std::vector<SCH_ITEM*> candidates;
141 std::set<SCH_ITEM*> strong_drivers;
142
143 m_driver = nullptr;
144
145 // Hierarchical labels are lower priority than local labels here,
146 // because on the first pass we want local labels to drive subgraphs
147 // so that we can identify same-sheet neighbors and link them together.
148 // Hierarchical labels will end up overriding the final net name if
149 // a higher-level sheet has a different name during the hierarchical
150 // pass.
151
152 for( SCH_ITEM* item : m_drivers )
153 {
154 PRIORITY item_priority = GetDriverPriority( item );
155
156 if( item_priority == PRIORITY::PIN )
157 {
158 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
159
160 if( !static_cast<SCH_SYMBOL*>( pin->GetParentSymbol() )->IsInNetlist() )
161 continue;
162 }
163
164 if( item_priority >= PRIORITY::HIER_LABEL )
165 strong_drivers.insert( item );
166
167 if( item_priority > highest_priority )
168 {
169 candidates.clear();
170 candidates.push_back( item );
171 highest_priority = item_priority;
172 }
173 else if( !candidates.empty() && ( item_priority == highest_priority ) )
174 {
175 candidates.push_back( item );
176 }
177 }
178
179 if( highest_priority >= PRIORITY::HIER_LABEL )
180 m_strong_driver = true;
181
182 // Power pins are 5, global labels are 6
183 m_local_driver = ( highest_priority < PRIORITY::GLOBAL_POWER_PIN );
184
185 if( !candidates.empty() )
186 {
187 // Candidates are ranked using the following rules (in order):
188 // 1. Prefer bus supersets over subsets to keep the widest connection.
189 // 2. Prefer pins on power symbols (global first, then local) over regular pins.
190 // 3. Prefer output sheet pins over input sheet pins.
191 // 4. Prefer names that do not look auto-generated (avoid "-Pad" suffixes).
192 // 5. Fall back to alphabetical comparison for deterministic ordering.
193 auto candidate_cmp = [&]( SCH_ITEM* a, SCH_ITEM* b )
194 {
195 SCH_CONNECTION* ac = a->Connection( &m_sheet );
196 SCH_CONNECTION* bc = b->Connection( &m_sheet );
197
198 if( ac->IsBus() && bc->IsBus() )
199 {
200 if( bc->IsSubsetOf( ac ) )
201 return true;
202 if( ac->IsSubsetOf( bc ) )
203 return false;
204 }
205
206 if( a->Type() == SCH_PIN_T && b->Type() == SCH_PIN_T )
207 {
208 SCH_PIN* pa = static_cast<SCH_PIN*>( a );
209 SCH_PIN* pb = static_cast<SCH_PIN*>( b );
210
211 bool aGlobal = pa->GetLibPin()->GetParentSymbol()->IsGlobalPower();
212 bool bGlobal = pb->GetLibPin()->GetParentSymbol()->IsGlobalPower();
213
214 if( aGlobal != bGlobal )
215 return aGlobal;
216
217 bool aLocal = pa->GetLibPin()->GetParentSymbol()->IsLocalPower();
218 bool bLocal = pb->GetLibPin()->GetParentSymbol()->IsLocalPower();
219
220 if( aLocal != bLocal )
221 return aLocal;
222 }
223
224 if( a->Type() == SCH_SHEET_PIN_T && b->Type() == SCH_SHEET_PIN_T )
225 {
226 SCH_SHEET_PIN* sa = static_cast<SCH_SHEET_PIN*>( a );
227 SCH_SHEET_PIN* sb = static_cast<SCH_SHEET_PIN*>( b );
228
229 if( sa->GetShape() != sb->GetShape() )
230 {
231 if( sa->GetShape() == LABEL_FLAG_SHAPE::L_OUTPUT )
232 return true;
233 if( sb->GetShape() == LABEL_FLAG_SHAPE::L_OUTPUT )
234 return false;
235 }
236 }
237
238 const wxString& a_name = GetNameForDriver( a );
239 const wxString& b_name = GetNameForDriver( b );
240
241 bool a_lowQualityName = a_name.Contains( "-Pad" );
242 bool b_lowQualityName = b_name.Contains( "-Pad" );
243
244 if( a_lowQualityName != b_lowQualityName )
245 return !a_lowQualityName;
246
247 return a_name < b_name;
248 };
249
250 std::sort( candidates.begin(), candidates.end(), candidate_cmp );
251
252 m_driver = candidates.front();
253 }
254
255 if( strong_drivers.size() > 1 )
256 m_multiple_drivers = true;
257
258 // Drop weak drivers
259 if( m_strong_driver )
260 {
261 m_drivers.clear();
262 m_drivers.insert( strong_drivers.begin(), strong_drivers.end() );
263 }
264
265 // Cache driver connection
266 if( m_driver )
267 {
272 }
273 else if( !m_is_bus_member )
274 {
275 m_driver_connection = nullptr;
276 }
277
278 return ( m_driver != nullptr );
279}
280
281
283 SCH_ITEM*>>& aItems,
284 std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
285{
286 CONNECTION_SUBGRAPH* sg = this;
287
288 while( sg->m_absorbed_by )
289 {
290 wxCHECK2( sg->m_graph == sg->m_absorbed_by->m_graph, continue );
291 sg = sg->m_absorbed_by;
292 }
293
294 // If we are unable to insert the subgraph into the set, then we have already
295 // visited it and don't need to add it again.
296 if( aSubgraphs.insert( sg ).second == false )
297 return;
298
299 aSubgraphs.insert( sg->m_absorbed_subgraphs.begin(), sg->m_absorbed_subgraphs.end() );
300
301 for( SCH_ITEM* item : sg->m_items )
302 aItems.emplace( m_sheet, item );
303
304 for( CONNECTION_SUBGRAPH* child_sg : sg->m_hier_children )
305 child_sg->getAllConnectedItems( aItems, aSubgraphs );
306}
307
308
310{
311 if( !m_driver || m_dirty )
312 return "";
313
314 if( !m_driver->Connection( &m_sheet ) )
315 {
316#ifdef CONNECTIVITY_DEBUG
317 wxASSERT_MSG( false, wxS( "Tried to get the net name of an item with no connection" ) );
318#endif
319
320 return "";
321 }
322
323 return m_driver->Connection( &m_sheet )->Name();
324}
325
326
327std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetAllBusLabels() const
328{
329 std::vector<SCH_ITEM*> labels;
330
331 for( SCH_ITEM* item : m_drivers )
332 {
333 switch( item->Type() )
334 {
335 case SCH_LABEL_T:
337 {
338 CONNECTION_TYPE type = item->Connection( &m_sheet )->Type();
339
340 // Only consider bus vectors
341 if( type == CONNECTION_TYPE::BUS || type == CONNECTION_TYPE::BUS_GROUP )
342 labels.push_back( item );
343
344 break;
345 }
346
347 default:
348 break;
349 }
350 }
351
352 return labels;
353}
354
355
356std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetVectorBusLabels() const
357{
358 std::vector<SCH_ITEM*> labels;
359
360 for( SCH_ITEM* item : m_drivers )
361 {
362 switch( item->Type() )
363 {
364 case SCH_LABEL_T:
366 {
367 SCH_CONNECTION* label_conn = item->Connection( &m_sheet );
368
369 // Only consider bus vectors
370 if( label_conn->Type() == CONNECTION_TYPE::BUS )
371 labels.push_back( item );
372
373 break;
374 }
375
376 default:
377 break;
378 }
379 }
380
381 return labels;
382}
383
384
386{
387 switch( aItem->Type() )
388 {
389 case SCH_PIN_T:
390 {
391 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
392 bool forceNoConnect = m_no_connect != nullptr;
393
394 return pin->GetDefaultNetName( m_sheet, forceNoConnect );
395 }
396
397 case SCH_LABEL_T:
399 case SCH_HIER_LABEL_T:
400 {
401 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
402
403 // NB: any changes here will need corresponding changes in SCH_LABEL_BASE::cacheShownText()
404 return EscapeString( label->GetShownText( &m_sheet, false ), CTX_NETNAME );
405 }
406
407 case SCH_SHEET_PIN_T:
408 {
409 // Sheet pins need to use their parent sheet as their starting sheet or they will resolve
410 // variables on the current sheet first
411 SCH_SHEET_PIN* sheetPin = static_cast<SCH_SHEET_PIN*>( aItem );
413
414 if( path.Last() != sheetPin->GetParent() )
415 path.push_back( sheetPin->GetParent() );
416
417 return EscapeString( sheetPin->GetShownText( &path, false ), CTX_NETNAME );
418 }
419
420 default:
421 wxFAIL_MSG( wxS( "Unhandled item type in GetNameForDriver" ) );
422 return wxEmptyString;
423 }
424}
425
426
427const wxString& CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem ) const
428{
429 if( aItem->HasCachedDriverName() )
430 return aItem->GetCachedDriverName();
431
432 std::lock_guard lock( m_driver_name_cache_mutex );
433 auto it = m_driver_name_cache.find( aItem );
434
435 if( it != m_driver_name_cache.end() )
436 return it->second;
437
438 return m_driver_name_cache.emplace( aItem, driverName( aItem ) ).first->second;
439}
440
441
442const std::vector<std::pair<wxString, SCH_ITEM*>>
444{
445 std::vector<std::pair<wxString, SCH_ITEM*>> foundNetclasses;
446
447 const std::unordered_set<SCH_RULE_AREA*>& ruleAreaCache = aItem->GetRuleAreaCache();
448
449 // Get netclasses on attached rule areas
450 for( SCH_RULE_AREA* ruleArea : ruleAreaCache )
451 {
452 const std::vector<std::pair<wxString, SCH_ITEM*>> ruleNetclasses =
453 ruleArea->GetResolvedNetclasses();
454
455 if( ruleNetclasses.size() > 0 )
456 {
457 foundNetclasses.insert( foundNetclasses.end(), ruleNetclasses.begin(),
458 ruleNetclasses.end() );
459 }
460 }
461
462 // Get netclasses on child fields
463 aItem->RunOnChildren(
464 [&]( SCH_ITEM* aChild )
465 {
466 if( aChild->Type() == SCH_FIELD_T )
467 {
468 SCH_FIELD* field = static_cast<SCH_FIELD*>( aChild );
469
470 if( field->GetCanonicalName() == wxT( "Netclass" ) )
471 {
472 wxString netclass = field->GetShownText( &m_sheet, false );
473
474 if( netclass != wxEmptyString )
475 foundNetclasses.push_back( { netclass, aItem } );
476 }
477 }
478 },
480
481 std::sort(
482 foundNetclasses.begin(), foundNetclasses.end(),
483 []( const std::pair<wxString, SCH_ITEM*>& i1, const std::pair<wxString, SCH_ITEM*>& i2 )
484 {
485 return i1.first < i2.first;
486 } );
487
488 return foundNetclasses;
489}
490
491
493{
494 wxCHECK( m_sheet == aOther->m_sheet, /* void */ );
495
496 for( SCH_ITEM* item : aOther->m_items )
497 {
499 AddItem( item );
500 }
501
502 m_absorbed_subgraphs.insert( aOther );
503 m_absorbed_subgraphs.insert( aOther->m_absorbed_subgraphs.begin(),
504 aOther->m_absorbed_subgraphs.end() );
505
506 m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
507 m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
508
510
511 std::function<void( CONNECTION_SUBGRAPH* )> set_absorbed_by =
512 [ & ]( CONNECTION_SUBGRAPH *child )
513 {
514 child->m_absorbed_by = this;
515
516 for( CONNECTION_SUBGRAPH* subchild : child->m_absorbed_subgraphs )
517 set_absorbed_by( subchild );
518 };
519
520 aOther->m_absorbed = true;
521 aOther->m_dirty = false;
522 aOther->m_driver = nullptr;
523 aOther->m_driver_connection = nullptr;
524
525 set_absorbed_by( aOther );
526}
527
528
530{
531 m_items.insert( aItem );
532
533 if( aItem->Connection( &m_sheet )->IsDriver() )
534 m_drivers.insert( aItem );
535
536 if( aItem->Type() == SCH_SHEET_PIN_T )
537 m_hier_pins.insert( static_cast<SCH_SHEET_PIN*>( aItem ) );
538 else if( aItem->Type() == SCH_HIER_LABEL_T )
539 m_hier_ports.insert( static_cast<SCH_HIERLABEL*>( aItem ) );
540}
541
542
544{
546 return;
547
548 for( SCH_ITEM* item : m_items )
549 {
550 SCH_CONNECTION* item_conn = item->GetOrInitConnection( m_sheet, m_graph );
551
552 if( !item_conn )
553 continue;
554
555 if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
556 ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
557 {
558 continue;
559 }
560
561 if( item != m_driver )
562 {
563 item_conn->Clone( *m_driver_connection );
564 item_conn->ClearDirty();
565 }
566 }
567}
568
569
571{
572 if( !aDriver )
573 return PRIORITY::NONE;
574
575 switch( aDriver->Type() )
576 {
581 case SCH_PIN_T:
582 {
583 SCH_PIN* sch_pin = static_cast<SCH_PIN*>( aDriver );
584 const SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( sch_pin->GetParentSymbol() );
585
586 if( sch_pin->IsGlobalPower() )
588 else if( sch_pin->IsLocalPower() )
590 else if( !sym || sym->GetExcludedFromBoard()
591 || sym->GetLibSymbolRef()->GetReferenceField().GetText().StartsWith( '#' ) )
592 return PRIORITY::NONE;
593 else
594 return PRIORITY::PIN;
595 }
596
597 default: return PRIORITY::NONE;
598 }
599}
600
601
603{
604 std::copy( aGraph.m_items.begin(), aGraph.m_items.end(),
605 std::back_inserter( m_items ) );
606
607 for( SCH_ITEM* item : aGraph.m_items )
608 item->SetConnectionGraph( this );
609
610 std::copy( aGraph.m_subgraphs.begin(), aGraph.m_subgraphs.end(),
611 std::back_inserter( m_subgraphs ) );
612
613 for( CONNECTION_SUBGRAPH* sg : aGraph.m_subgraphs )
614 {
615 if( sg->m_driver_connection )
616 sg->m_driver_connection->SetGraph( this );
617
618 sg->m_graph = this;
619 }
620
621 std::copy( aGraph.m_driver_subgraphs.begin(),
622 aGraph.m_driver_subgraphs.end(),
623 std::back_inserter( m_driver_subgraphs ) );
624
625 std::copy( aGraph.m_global_power_pins.begin(),
626 aGraph.m_global_power_pins.end(),
627 std::back_inserter( m_global_power_pins ) );
628
629 for( auto& [key, value] : aGraph.m_net_name_to_subgraphs_map )
630 m_net_name_to_subgraphs_map.insert_or_assign( key, value );
631
632 for( auto& [key, value] : aGraph.m_sheet_to_subgraphs_map )
633 m_sheet_to_subgraphs_map.insert_or_assign( key, value );
634
635 for( auto& [key, value] : aGraph.m_net_name_to_code_map )
636 m_net_name_to_code_map.insert_or_assign( key, value );
637
638 for( auto& [key, value] : aGraph.m_bus_name_to_code_map )
639 m_bus_name_to_code_map.insert_or_assign( key, value );
640
641 for( auto& [key, value] : aGraph.m_net_code_to_subgraphs_map )
642 m_net_code_to_subgraphs_map.insert_or_assign( key, value );
643
644 for( auto& [key, value] : aGraph.m_item_to_subgraph_map )
645 m_item_to_subgraph_map.insert_or_assign( key, value );
646
647 for( auto& [key, value] : aGraph.m_local_label_cache )
648 m_local_label_cache.insert_or_assign( key, value );
649
650 for( auto& [key, value] : aGraph.m_global_label_cache )
651 m_global_label_cache.insert_or_assign( key, value );
652
653 m_last_bus_code = std::max( m_last_bus_code, aGraph.m_last_bus_code );
654 m_last_net_code = std::max( m_last_net_code, aGraph.m_last_net_code );
656
657}
658
659
661{
662 wxCHECK2( aOldItem->Type() == aNewItem->Type(), return );
663
664 auto exchange = [&]( SCH_ITEM* aOld, SCH_ITEM* aNew )
665 {
666 auto it = m_item_to_subgraph_map.find( aOld );
667
668 if( it == m_item_to_subgraph_map.end() )
669 return;
670
671 CONNECTION_SUBGRAPH* sg = it->second;
672
673 sg->ExchangeItem( aOld, aNew );
674
675 m_item_to_subgraph_map.erase( it );
676 m_item_to_subgraph_map.emplace( aNew, sg );
677
678 for( auto it2 = m_items.begin(); it2 != m_items.end(); ++it2 )
679 {
680 if( *it2 == aOld )
681 {
682 *it2 = aNew;
683 break;
684 }
685 }
686 };
687
688 exchange( aOldItem, aNewItem );
689
690 if( aOldItem->Type() == SCH_SYMBOL_T )
691 {
692 SCH_SYMBOL* oldSymbol = static_cast<SCH_SYMBOL*>( aOldItem );
693 SCH_SYMBOL* newSymbol = static_cast<SCH_SYMBOL*>( aNewItem );
694 std::vector<SCH_PIN*> oldPins = oldSymbol->GetPins( &m_schematic->CurrentSheet() );
695 std::vector<SCH_PIN*> newPins = newSymbol->GetPins( &m_schematic->CurrentSheet() );
696
697 wxCHECK2( oldPins.size() == newPins.size(), return );
698
699 for( size_t ii = 0; ii < oldPins.size(); ii++ )
700 {
701 exchange( oldPins[ii], newPins[ii] );
702 }
703 }
704}
705
706
708{
709 for( auto& subgraph : m_subgraphs )
710 {
712 if( subgraph->m_graph == this )
713 delete subgraph;
714 }
715
716 m_items.clear();
717 m_subgraphs.clear();
718 m_driver_subgraphs.clear();
720 m_global_power_pins.clear();
721 m_bus_alias_cache.clear();
727 m_local_label_cache.clear();
728 m_global_label_cache.clear();
729 m_last_net_code = 1;
730 m_last_bus_code = 1;
732}
733
734
735void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional,
736 std::function<void( SCH_ITEM* )>* aChangedItemHandler,
737 PROGRESS_REPORTER* aProgressReporter )
738{
739 APP_MONITOR::TRANSACTION monitorTrans( "CONNECTION_GRAPH::Recalculate", "Recalculate" );
740 PROF_TIMER recalc_time( "CONNECTION_GRAPH::Recalculate" );
741 monitorTrans.Start();
742
743 if( aUnconditional )
744 Reset();
745
746 monitorTrans.StartSpan( "updateItemConnectivity", "" );
747 PROF_TIMER update_items( "updateItemConnectivity" );
748
749 m_sheetList = aSheetList;
750 std::set<SCH_ITEM*> dirty_items;
751
752 int count = aSheetList.size() * 2;
753 int done = 0;
754
755 for( const SCH_SHEET_PATH& sheet : aSheetList )
756 {
757 if( aProgressReporter )
758 {
759 aProgressReporter->SetCurrentProgress( done++ / (double) count );
760 aProgressReporter->KeepRefreshing();
761 }
762
763 std::vector<SCH_ITEM*> items;
764
765 // Store current unit value, to replace it after calculations
766 std::vector<std::pair<SCH_SYMBOL*, int>> symbolsChanged;
767
768 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
769 {
770 if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
771 {
772 wxLogTrace( ConnTrace, wxT( "Adding item %s to connectivity graph update" ),
773 item->GetTypeDesc() );
774 items.push_back( item );
775 dirty_items.insert( item );
776
777 // Add any symbol dirty pins to the dirty_items list
778 if( item->Type() == SCH_SYMBOL_T )
779 {
780 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
781
782 for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
783 {
784 if( pin->IsConnectivityDirty() )
785 {
786 dirty_items.insert( pin );
787 }
788 }
789 }
790 }
791 // If the symbol isn't dirty, look at the pins
792 // TODO: remove symbols from connectivity graph and only use pins
793 else if( item->Type() == SCH_SYMBOL_T )
794 {
795 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
796
797 for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
798 {
799 if( pin->IsConnectivityDirty() )
800 {
801 items.push_back( pin );
802 dirty_items.insert( pin );
803 }
804 }
805 }
806 else if( item->Type() == SCH_SHEET_T )
807 {
808 SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
809
810 for( SCH_SHEET_PIN* pin : sheetItem->GetPins() )
811 {
812 if( pin->IsConnectivityDirty() )
813 {
814 items.push_back( pin );
815 dirty_items.insert( pin );
816 }
817 }
818 }
819
820 // Ensure the hierarchy info stored in the SCH_SCREEN (such as symbol units) reflects
821 // the current SCH_SHEET_PATH
822 if( item->Type() == SCH_SYMBOL_T )
823 {
824 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
825 int new_unit = symbol->GetUnitSelection( &sheet );
826
827 // Store the initial unit value so we can restore it after calculations
828 if( symbol->GetUnit() != new_unit )
829 symbolsChanged.push_back( { symbol, symbol->GetUnit() } );
830
831 symbol->SetUnit( new_unit );
832 }
833 }
834
835 m_items.reserve( m_items.size() + items.size() );
836
837 updateItemConnectivity( sheet, items );
838
839 if( aProgressReporter )
840 {
841 aProgressReporter->SetCurrentProgress( done++ / count );
842 aProgressReporter->KeepRefreshing();
843 }
844
845 // UpdateDanglingState() also adds connected items for SCH_TEXT
846 sheet.LastScreen()->TestDanglingEnds( &sheet, aChangedItemHandler );
847
848 // Restore the m_unit member variables where we had to change them
849 for( const auto& [ symbol, originalUnit ] : symbolsChanged )
850 symbol->SetUnit( originalUnit );
851 }
852
853 // Restore the dangling states of items in the current SCH_SCREEN to match the current
854 // SCH_SHEET_PATH.
855 m_schematic->CurrentSheet().LastScreen()->TestDanglingEnds( &m_schematic->CurrentSheet(),
856 aChangedItemHandler );
857
858 for( SCH_ITEM* item : dirty_items )
859 item->SetConnectivityDirty( false );
860
861
862 monitorTrans.FinishSpan();
863 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
864 update_items.Show();
865
866 PROF_TIMER build_graph( "buildConnectionGraph" );
867 monitorTrans.StartSpan( "BuildConnectionGraph", "" );
868
869 buildConnectionGraph( aChangedItemHandler, aUnconditional );
870
871 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
872 build_graph.Show();
873
874 monitorTrans.FinishSpan();
875
876 recalc_time.Stop();
877
878 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
879 recalc_time.Show();
880
881 monitorTrans.Finish();
882}
883
884
885std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> CONNECTION_GRAPH::ExtractAffectedItems(
886 const std::set<SCH_ITEM*> &aItems )
887{
888 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> retvals;
889 std::set<CONNECTION_SUBGRAPH*> subgraphs;
890
891 auto traverse_subgraph = [&retvals, &subgraphs]( CONNECTION_SUBGRAPH* aSubgraph )
892 {
893 // Find the primary subgraph on this sheet
894 while( aSubgraph->m_absorbed_by )
895 {
896 // Should we skip this if the absorbed by sub-graph is not this sub-grap?
897 wxASSERT( aSubgraph->m_graph == aSubgraph->m_absorbed_by->m_graph );
898 aSubgraph = aSubgraph->m_absorbed_by;
899 }
900
901 // Find the top most connected subgraph on all sheets
902 while( aSubgraph->m_hier_parent )
903 {
904 // Should we skip this if the absorbed by sub-graph is not this sub-grap?
905 wxASSERT( aSubgraph->m_graph == aSubgraph->m_hier_parent->m_graph );
906 aSubgraph = aSubgraph->m_hier_parent;
907 }
908
909 // Recurse through all subsheets to collect connected items
910 aSubgraph->getAllConnectedItems( retvals, subgraphs );
911 };
912
913 auto extract_element = [&]( SCH_ITEM* aItem )
914 {
915 CONNECTION_SUBGRAPH* item_sg = GetSubgraphForItem( aItem );
916
917 if( !item_sg )
918 {
919 wxLogTrace( ConnTrace, wxT( "Item %s not found in connection graph" ),
920 aItem->GetTypeDesc() );
921 return;
922 }
923
924 if( !item_sg->ResolveDrivers( true ) )
925 {
926 wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld (%p) has no driver" ),
927 aItem->GetTypeDesc(), item_sg->m_code, item_sg );
928 }
929
930 std::vector<CONNECTION_SUBGRAPH*> sg_to_scan = GetAllSubgraphs( item_sg->GetNetName() );
931
932 if( sg_to_scan.empty() )
933 {
934 wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld with net %s has no neighbors" ),
935 aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName() );
936 sg_to_scan.push_back( item_sg );
937 }
938
939 wxLogTrace( ConnTrace,
940 wxT( "Removing all item %s connections from subgraph %ld with net %s: Found "
941 "%zu subgraphs" ),
942 aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName(),
943 sg_to_scan.size() );
944
945 for( CONNECTION_SUBGRAPH* sg : sg_to_scan )
946 {
947 traverse_subgraph( sg );
948
949 for( auto& bus_it : sg->m_bus_neighbors )
950 {
951 for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
952 traverse_subgraph( bus_sg );
953 }
954
955 for( auto& bus_it : sg->m_bus_parents )
956 {
957 for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
958 traverse_subgraph( bus_sg );
959 }
960 }
961
962 std::erase( m_items, aItem );
963 };
964
965 for( SCH_ITEM* item : aItems )
966 {
967 if( item->Type() == SCH_SHEET_T )
968 {
969 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
970
971 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
972 extract_element( pin );
973 }
974 else if ( item->Type() == SCH_SYMBOL_T )
975 {
976 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
977
978 for( SCH_PIN* pin : symbol->GetPins( &m_schematic->CurrentSheet() ) )
979 extract_element( pin );
980 }
981 else
982 {
983 extract_element( item );
984 }
985 }
986
987 removeSubgraphs( subgraphs );
988
989 for( const auto& [path, item] : retvals )
990 std::erase( m_items, item );
991
992 return retvals;
993}
994
995
997{
998 auto it = m_item_to_subgraph_map.find( aItem );
999
1000 if( it == m_item_to_subgraph_map.end() )
1001 return;
1002
1003 CONNECTION_SUBGRAPH* subgraph = it->second;
1004
1005 while(subgraph->m_absorbed_by )
1006 subgraph = subgraph->m_absorbed_by;
1007
1008 subgraph->RemoveItem( aItem );
1009 std::erase( m_items, aItem );
1010 m_item_to_subgraph_map.erase( it );
1011}
1012
1013
1014void CONNECTION_GRAPH::removeSubgraphs( std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
1015{
1016 wxLogTrace( ConnTrace, wxT( "Removing %zu subgraphs" ), aSubgraphs.size() );
1017 std::sort( m_driver_subgraphs.begin(), m_driver_subgraphs.end() );
1018 std::sort( m_subgraphs.begin(), m_subgraphs.end() );
1019 std::set<int> codes_to_remove;
1020
1021 for( auto& el : m_sheet_to_subgraphs_map )
1022 {
1023 std::sort( el.second.begin(), el.second.end() );
1024 }
1025
1026 for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
1027 {
1028 for( auto& it : sg->m_bus_neighbors )
1029 {
1030 for( CONNECTION_SUBGRAPH* neighbor : it.second )
1031 {
1032 auto& parents = neighbor->m_bus_parents[it.first];
1033
1034 for( auto test = parents.begin(); test != parents.end(); )
1035 {
1036 if( *test == sg )
1037 test = parents.erase( test );
1038 else
1039 ++test;
1040 }
1041
1042 if( parents.empty() )
1043 neighbor->m_bus_parents.erase( it.first );
1044 }
1045 }
1046
1047 for( auto& it : sg->m_bus_parents )
1048 {
1049 for( CONNECTION_SUBGRAPH* parent : it.second )
1050 {
1051 auto& neighbors = parent->m_bus_neighbors[it.first];
1052
1053 for( auto test = neighbors.begin(); test != neighbors.end(); )
1054 {
1055 if( *test == sg )
1056 test = neighbors.erase( test );
1057 else
1058 ++test;
1059 }
1060
1061 if( neighbors.empty() )
1062 parent->m_bus_neighbors.erase( it.first );
1063 }
1064 }
1065
1066 {
1067 auto it = std::lower_bound( m_driver_subgraphs.begin(), m_driver_subgraphs.end(), sg );
1068
1069 while( it != m_driver_subgraphs.end() && *it == sg )
1070 it = m_driver_subgraphs.erase( it );
1071 }
1072
1073 {
1074 auto it = std::lower_bound( m_subgraphs.begin(), m_subgraphs.end(), sg );
1075
1076 while( it != m_subgraphs.end() && *it == sg )
1077 it = m_subgraphs.erase( it );
1078 }
1079
1080 for( auto& el : m_sheet_to_subgraphs_map )
1081 {
1082 auto it = std::lower_bound( el.second.begin(), el.second.end(), sg );
1083
1084 while( it != el.second.end() && *it == sg )
1085 it = el.second.erase( it );
1086 }
1087
1088 auto remove_sg = [sg]( auto it ) -> bool
1089 {
1090 for( const CONNECTION_SUBGRAPH* test_sg : it->second )
1091 {
1092 if( sg == test_sg )
1093 return true;
1094 }
1095
1096 return false;
1097 };
1098
1099 for( auto it = m_global_label_cache.begin(); it != m_global_label_cache.end(); )
1100 {
1101 if( remove_sg( it ) )
1102 it = m_global_label_cache.erase( it );
1103 else
1104 ++it;
1105 }
1106
1107 for( auto it = m_local_label_cache.begin(); it != m_local_label_cache.end(); )
1108 {
1109 if( remove_sg( it ) )
1110 it = m_local_label_cache.erase( it );
1111 else
1112 ++it;
1113 }
1114
1115 for( auto it = m_net_code_to_subgraphs_map.begin();
1116 it != m_net_code_to_subgraphs_map.end(); )
1117 {
1118 if( remove_sg( it ) )
1119 {
1120 codes_to_remove.insert( it->first.Netcode );
1121 it = m_net_code_to_subgraphs_map.erase( it );
1122 }
1123 else
1124 {
1125 ++it;
1126 }
1127 }
1128
1129 for( auto it = m_net_name_to_subgraphs_map.begin();
1130 it != m_net_name_to_subgraphs_map.end(); )
1131 {
1132 if( remove_sg( it ) )
1133 it = m_net_name_to_subgraphs_map.erase( it );
1134 else
1135 ++it;
1136 }
1137
1138 for( auto it = m_item_to_subgraph_map.begin(); it != m_item_to_subgraph_map.end(); )
1139 {
1140 if( it->second == sg )
1141 it = m_item_to_subgraph_map.erase( it );
1142 else
1143 ++it;
1144 }
1145
1146
1147 }
1148
1149 for( auto it = m_net_name_to_code_map.begin(); it != m_net_name_to_code_map.end(); )
1150 {
1151 if( codes_to_remove.contains( it->second ) )
1152 it = m_net_name_to_code_map.erase( it );
1153 else
1154 ++it;
1155 }
1156
1157 for( auto it = m_bus_name_to_code_map.begin(); it != m_bus_name_to_code_map.end(); )
1158 {
1159 if( codes_to_remove.contains( it->second ) )
1160 it = m_bus_name_to_code_map.erase( it );
1161 else
1162 ++it;
1163 }
1164
1165 for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
1166 {
1167 sg->m_code = -1;
1168 sg->m_graph = nullptr;
1169 delete sg;
1170 }
1171}
1172
1173
1175 SCH_SYMBOL* aSymbol,
1176 std::map<VECTOR2I, std::vector<SCH_ITEM*>>& aConnectionMap )
1177{
1178 auto updatePin = [&]( SCH_PIN* aPin, SCH_CONNECTION* aConn )
1179 {
1180 aConn->SetType( CONNECTION_TYPE::NET );
1181 wxString name = aPin->GetDefaultNetName( aSheet );
1182 aPin->ClearConnectedItems( aSheet );
1183
1184 if( aPin->IsGlobalPower() )
1185 {
1186 aConn->SetName( name );
1187 m_global_power_pins.emplace_back( std::make_pair( aSheet, aPin ) );
1188 }
1189 };
1190
1191 std::map<wxString, std::vector<SCH_PIN*>> pinNumberMap;
1192
1193 for( SCH_PIN* pin : aSymbol->GetPins( &aSheet ) )
1194 {
1195 m_items.emplace_back( pin );
1196 SCH_CONNECTION* conn = pin->InitializeConnection( aSheet, this );
1197 updatePin( pin, conn );
1198 aConnectionMap[ pin->GetPosition() ].push_back( pin );
1199 pinNumberMap[pin->GetNumber()].emplace_back( pin );
1200 }
1201
1202 auto linkPinsInVec = [&]( const std::vector<SCH_PIN*>& aVec )
1203 {
1204 for( size_t i = 0; i < aVec.size(); ++i )
1205 {
1206 for( size_t j = i + 1; j < aVec.size(); ++j )
1207 {
1208 aVec[i]->AddConnectionTo( aSheet, aVec[j] );
1209 aVec[j]->AddConnectionTo( aSheet, aVec[i] );
1210 }
1211 }
1212 };
1213
1214 if( aSymbol->GetLibSymbolRef() )
1215 {
1216 if( aSymbol->GetLibSymbolRef()->GetDuplicatePinNumbersAreJumpers() )
1217 {
1218 for( const auto& [number, group] : pinNumberMap )
1219 linkPinsInVec( group );
1220 }
1221
1222 for( const std::set<wxString>& group : aSymbol->GetLibSymbolRef()->JumperPinGroups() )
1223 {
1224 std::vector<SCH_PIN*> pins;
1225
1226 for( const wxString& pinNumber : group )
1227 pins.emplace_back( aSymbol->GetPin( pinNumber ) );
1228
1229 linkPinsInVec( pins );
1230 }
1231 }
1232}
1233
1234
1236{
1237 aConn->SetType( CONNECTION_TYPE::NET );
1238
1239 // because calling the first time is not thread-safe
1240 wxString name = aPin->GetDefaultNetName( aSheet );
1241 aPin->ClearConnectedItems( aSheet );
1242
1243 if( aPin->IsGlobalPower() )
1244 {
1245 aConn->SetName( name );
1246 m_global_power_pins.emplace_back( std::make_pair( aSheet, aPin ) );
1247 }
1248}
1249
1250
1252 std::map<VECTOR2I, std::vector<SCH_ITEM*>>& aConnectionMap )
1253{
1254 std::vector<VECTOR2I> points = aItem->GetConnectionPoints();
1255 aItem->ClearConnectedItems( aSheet );
1256
1257 m_items.emplace_back( aItem );
1258 SCH_CONNECTION* conn = aItem->InitializeConnection( aSheet, this );
1259
1260 switch( aItem->Type() )
1261 {
1262 case SCH_LINE_T:
1263 conn->SetType( aItem->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS : CONNECTION_TYPE::NET );
1264 break;
1265
1267 conn->SetType( CONNECTION_TYPE::BUS );
1268 static_cast<SCH_BUS_BUS_ENTRY*>( aItem )->m_connected_bus_items[0] = nullptr;
1269 static_cast<SCH_BUS_BUS_ENTRY*>( aItem )->m_connected_bus_items[1] = nullptr;
1270 break;
1271
1272 case SCH_PIN_T:
1273 if( points.empty() )
1274 points = { static_cast<SCH_PIN*>( aItem )->GetPosition() };
1275
1276 updatePinConnectivity( aSheet, static_cast<SCH_PIN*>( aItem ), conn );
1277 break;
1278
1280 conn->SetType( CONNECTION_TYPE::NET );
1281 static_cast<SCH_BUS_WIRE_ENTRY*>( aItem )->m_connected_bus_item = nullptr;
1282 break;
1283
1284 default: break;
1285 }
1286
1287 for( const VECTOR2I& point : points )
1288 aConnectionMap[point].push_back( aItem );
1289}
1290
1291
1293 const std::vector<SCH_ITEM*>& aItemList )
1294{
1295 wxLogTrace( wxT( "Updating connectivity for sheet %s with %zu items" ),
1296 aSheet.Last()->GetFileName(), aItemList.size() );
1297 std::map<VECTOR2I, std::vector<SCH_ITEM*>> connection_map;
1298
1299 for( SCH_ITEM* item : aItemList )
1300 {
1301 std::vector<VECTOR2I> points = item->GetConnectionPoints();
1302 item->ClearConnectedItems( aSheet );
1303
1304 if( item->Type() == SCH_SHEET_T )
1305 {
1306 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
1307 {
1308 pin->InitializeConnection( aSheet, this );
1309
1310 pin->ClearConnectedItems( aSheet );
1311
1312 connection_map[ pin->GetTextPos() ].push_back( pin );
1313 m_items.emplace_back( pin );
1314 }
1315 }
1316 else if( item->Type() == SCH_SYMBOL_T )
1317 {
1318 updateSymbolConnectivity( aSheet, static_cast<SCH_SYMBOL*>( item ), connection_map );
1319 }
1320 else
1321 {
1322 updateGenericItemConnectivity( aSheet, item, connection_map );
1323 }
1324 }
1325
1326 for( const auto& it : connection_map )
1327 {
1328 std::vector<SCH_ITEM*> connection_vec = it.second;
1329 std::sort( connection_vec.begin(), connection_vec.end() );
1330 alg::remove_duplicates( connection_vec );
1331
1332 // Pre-scan to see if we have a bus at this location
1333 SCH_LINE* busLine = aSheet.LastScreen()->GetBus( it.first );
1334
1335 for( SCH_ITEM* connected_item : connection_vec )
1336 {
1337 // Bus entries are special: they can have connection points in the
1338 // middle of a wire segment, because the junction algo doesn't split
1339 // the segment in two where you place a bus entry. This means that
1340 // bus entries that don't land on the end of a line segment need to
1341 // have "virtual" connection points to the segments they graphically
1342 // touch.
1343 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1344 {
1345 // If this location only has the connection point of the bus
1346 // entry itself, this means that either the bus entry is not
1347 // connected to anything graphically, or that it is connected to
1348 // a segment at some point other than at one of the endpoints.
1349 if( connection_vec.size() == 1 )
1350 {
1351 if( busLine )
1352 {
1353 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1354 bus_entry->m_connected_bus_item = busLine;
1355 }
1356 }
1357 }
1358
1359 // Bus-to-bus entries are treated just like bus wires
1360 else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
1361 {
1362 if( busLine )
1363 {
1364 auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
1365
1366 if( it.first == bus_entry->GetPosition() )
1367 bus_entry->m_connected_bus_items[0] = busLine;
1368 else
1369 bus_entry->m_connected_bus_items[1] = busLine;
1370
1371 bus_entry->AddConnectionTo( aSheet, busLine );
1372 busLine->AddConnectionTo( aSheet, bus_entry );
1373 continue;
1374 }
1375 }
1376
1377 // Change junctions to be on bus junction layer if they are touching a bus
1378 else if( connected_item->Type() == SCH_JUNCTION_T )
1379 {
1380 connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
1381 }
1382
1383 for( SCH_ITEM* test_item : connection_vec )
1384 {
1385 bool bus_connection_ok = true;
1386
1387 if( test_item == connected_item )
1388 continue;
1389
1390 // Set up the link between the bus entry net and the bus
1391 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1392 {
1393 if( test_item->GetLayer() == LAYER_BUS )
1394 {
1395 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1396 bus_entry->m_connected_bus_item = test_item;
1397 }
1398 }
1399
1400 // Bus entries only connect to bus lines on the end that is touching a bus line.
1401 // If the user has overlapped another net line with the endpoint of the bus entry
1402 // where the entry connects to a bus, we don't want to short-circuit it.
1403 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1404 {
1405 bus_connection_ok = !busLine || test_item->GetLayer() == LAYER_BUS;
1406 }
1407 else if( test_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1408 {
1409 bus_connection_ok = !busLine || connected_item->GetLayer() == LAYER_BUS;
1410 }
1411
1412 if( connected_item->ConnectionPropagatesTo( test_item ) &&
1413 test_item->ConnectionPropagatesTo( connected_item ) &&
1414 bus_connection_ok )
1415 {
1416 connected_item->AddConnectionTo( aSheet, test_item );
1417 }
1418 }
1419
1420 // If we got this far and did not find a connected bus item for a bus entry,
1421 // we should do a manual scan in case there is a bus item on this connection
1422 // point but we didn't pick it up earlier because there is *also* a net item here.
1423 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1424 {
1425 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1426
1427 if( !bus_entry->m_connected_bus_item )
1428 {
1429 SCH_SCREEN* screen = aSheet.LastScreen();
1430 SCH_LINE* bus = screen->GetBus( it.first );
1431
1432 if( bus )
1433 bus_entry->m_connected_bus_item = bus;
1434 }
1435 }
1436 }
1437 }
1438}
1439
1440
1442{
1443 // Recache all bus aliases for later use
1444 wxCHECK_RET( m_schematic, wxS( "Connection graph cannot be built without schematic pointer" ) );
1445
1446 SCH_SCREENS screens( m_schematic->Root() );
1447
1448 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1449 {
1450 for( const std::shared_ptr<BUS_ALIAS>& alias : screen->GetBusAliases() )
1451 m_bus_alias_cache[alias->GetName()] = alias;
1452 }
1453
1454 // Build subgraphs from items (on a per-sheet basis)
1455 for( SCH_ITEM* item : m_items )
1456 {
1457 for( const auto& it : item->m_connection_map )
1458 {
1459 const SCH_SHEET_PATH& sheet = it.first;
1460 SCH_CONNECTION* connection = it.second;
1461
1462 if( connection->SubgraphCode() == 0 )
1463 {
1464 CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
1465
1466 subgraph->m_code = m_last_subgraph_code++;
1467 subgraph->m_sheet = sheet;
1468
1469 subgraph->AddItem( item );
1470
1471 connection->SetSubgraphCode( subgraph->m_code );
1472 m_item_to_subgraph_map[item] = subgraph;
1473
1474 std::list<SCH_ITEM*> memberlist;
1475
1476 auto get_items =
1477 [&]( SCH_ITEM* aItem ) -> bool
1478 {
1479 SCH_CONNECTION* conn = aItem->GetOrInitConnection( sheet, this );
1480 bool unique = !( aItem->GetFlags() & CONNECTIVITY_CANDIDATE );
1481
1482 if( conn && !conn->SubgraphCode() )
1483 aItem->SetFlags( CONNECTIVITY_CANDIDATE );
1484
1485 return ( unique && conn && ( conn->SubgraphCode() == 0 ) );
1486 };
1487
1488 std::copy_if( item->ConnectedItems( sheet ).begin(),
1489 item->ConnectedItems( sheet ).end(),
1490 std::back_inserter( memberlist ), get_items );
1491
1492 for( SCH_ITEM* connected_item : memberlist )
1493 {
1494 if( connected_item->Type() == SCH_NO_CONNECT_T )
1495 subgraph->m_no_connect = connected_item;
1496
1497 SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
1498
1499 wxCHECK2( connected_conn, continue );
1500
1501 if( connected_conn->SubgraphCode() == 0 )
1502 {
1503 connected_conn->SetSubgraphCode( subgraph->m_code );
1504 m_item_to_subgraph_map[connected_item] = subgraph;
1505 subgraph->AddItem( connected_item );
1506 const SCH_ITEM_VEC& citemset = connected_item->ConnectedItems( sheet );
1507
1508 for( SCH_ITEM* citem : citemset )
1509 {
1510 if( citem->HasFlag( CONNECTIVITY_CANDIDATE ) )
1511 continue;
1512
1513 if( get_items( citem ) )
1514 memberlist.push_back( citem );
1515 }
1516 }
1517 }
1518
1519 for( SCH_ITEM* connected_item : memberlist )
1520 connected_item->ClearFlags( CONNECTIVITY_CANDIDATE );
1521
1522 subgraph->m_dirty = true;
1523 m_subgraphs.push_back( subgraph );
1524 }
1525 }
1526 }
1527
1528}
1529
1530
1532{
1533 // Resolve drivers for subgraphs and propagate connectivity info
1534 std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
1535
1536 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
1537 [&] ( const CONNECTION_SUBGRAPH* candidate )
1538 {
1539 return candidate->m_dirty;
1540 } );
1541
1542 wxLogTrace( ConnTrace, wxT( "Resolving drivers for %zu subgraphs" ), dirty_graphs.size() );
1543
1544 std::vector<std::future<size_t>> returns( dirty_graphs.size() );
1545
1546 auto update_lambda = []( CONNECTION_SUBGRAPH* subgraph ) -> size_t
1547 {
1548 if( !subgraph->m_dirty )
1549 return 0;
1550
1551 // Special processing for some items
1552 for( SCH_ITEM* item : subgraph->m_items )
1553 {
1554 switch( item->Type() )
1555 {
1556 case SCH_NO_CONNECT_T:
1557 subgraph->m_no_connect = item;
1558 break;
1559
1561 subgraph->m_bus_entry = item;
1562 break;
1563
1564 case SCH_PIN_T:
1565 {
1566 auto pin = static_cast<SCH_PIN*>( item );
1567
1568 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
1569 subgraph->m_no_connect = item;
1570
1571 break;
1572 }
1573
1574 default:
1575 break;
1576 }
1577 }
1578
1579 subgraph->ResolveDrivers( true );
1580 subgraph->m_dirty = false;
1581
1582 return 1;
1583 };
1584
1586
1587 auto results = tp.parallelize_loop( dirty_graphs.size(),
1588 [&]( const int a, const int b)
1589 {
1590 for( int ii = a; ii < b; ++ii )
1591 update_lambda( dirty_graphs[ii] );
1592 });
1593 results.wait();
1594
1595 // Now discard any non-driven subgraphs from further consideration
1596
1597 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
1598 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1599 {
1600 return candidate->m_driver;
1601 } );
1602}
1603
1604
1606{
1607 // Check for subgraphs with the same net name but only weak drivers.
1608 // For example, two wires that are both connected to hierarchical
1609 // sheet pins that happen to have the same name, but are not the same.
1610
1611 for( auto&& subgraph : m_driver_subgraphs )
1612 {
1613 wxString full_name = subgraph->m_driver_connection->Name();
1614 wxString name = subgraph->m_driver_connection->Name( true );
1615 m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
1616
1617 // For vector buses, we need to cache the prefix also, as two different instances of the
1618 // weakly driven pin may have the same prefix but different vector start and end. We need
1619 // to treat those as needing renaming also, because otherwise if they end up on a sheet with
1620 // common usage, they will be incorrectly merged.
1621 if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1622 {
1623 wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
1624 m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
1625 }
1626
1627 subgraph->m_dirty = true;
1628
1629 if( subgraph->m_strong_driver )
1630 {
1631 SCH_ITEM* driver = subgraph->m_driver;
1632 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1633
1634 switch( driver->Type() )
1635 {
1636 case SCH_LABEL_T:
1637 case SCH_HIER_LABEL_T:
1638 {
1639 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1640 break;
1641 }
1642 case SCH_GLOBAL_LABEL_T:
1643 {
1644 m_global_label_cache[name].push_back( subgraph );
1645 break;
1646 }
1647 case SCH_PIN_T:
1648 {
1649 SCH_PIN* pin = static_cast<SCH_PIN*>( driver );
1650 if( pin->IsGlobalPower() )
1651 {
1652 m_global_label_cache[name].push_back( subgraph );
1653 }
1654 else if( pin->IsLocalPower() )
1655 {
1656 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1657 }
1658 else
1659 {
1660 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
1661 wxLogTrace( ConnTrace, wxS( "Unexpected normal pin %s" ),
1662 driver->GetItemDescription( &unitsProvider, true ) );
1663 }
1664
1665 break;
1666 }
1667 default:
1668 {
1669 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
1670
1671 wxLogTrace( ConnTrace, wxS( "Unexpected strong driver %s" ),
1672 driver->GetItemDescription( &unitsProvider, true ) );
1673 break;
1674 }
1675 }
1676 }
1677 }
1678}
1679
1680
1682{
1683 std::vector<CONNECTION_SUBGRAPH*> new_subgraphs;
1684
1685 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1686 {
1687 SCH_ITEM_VEC vec = subgraph->GetAllBusLabels();
1688
1689 for( SCH_ITEM* item : vec )
1690 {
1691 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
1692
1693 SCH_CONNECTION dummy( item, subgraph->m_sheet );
1694 dummy.SetGraph( this );
1695 dummy.ConfigureFromLabel( label->GetShownText( &subgraph->m_sheet, false ) );
1696
1697 wxLogTrace( ConnTrace, wxS( "new bus label (%s)" ),
1698 label->GetShownText( &subgraph->m_sheet, false ) );
1699
1700 for( const auto& conn : dummy.Members() )
1701 {
1702 wxString name = conn->FullLocalName();
1703
1704 CONNECTION_SUBGRAPH* new_sg = new CONNECTION_SUBGRAPH( this );
1705
1706 // This connection cannot form a part of the item because the item is not, itself
1707 // connected to this subgraph. It exists as part of a virtual item that may be
1708 // connected to other items but is not in the schematic.
1709 SCH_CONNECTION* new_conn = new SCH_CONNECTION( item, subgraph->m_sheet );
1710 new_conn->SetGraph( this );
1711 new_conn->SetName( name );
1712 new_conn->SetType( CONNECTION_TYPE::NET );
1713 subgraph->StoreImplicitConnection( new_conn );
1714 int code = assignNewNetCode( *new_conn );
1715
1716 wxLogTrace( ConnTrace, wxS( "SG(%ld), Adding full local name (%s) with sg (%d) "
1717 "on subsheet %s" ),
1718 subgraph->m_code, name, code, subgraph->m_sheet.PathHumanReadable() );
1719
1720 new_sg->m_driver_connection = new_conn;
1721 new_sg->m_code = m_last_subgraph_code++;
1722 new_sg->m_sheet = subgraph->GetSheet();
1723 new_sg->m_is_bus_member = true;
1724 new_sg->m_strong_driver = true;
1725
1727 NET_NAME_CODE_CACHE_KEY key = { new_sg->GetNetName(), code };
1728 m_net_code_to_subgraphs_map[ key ].push_back( new_sg );
1729 m_net_name_to_subgraphs_map[ name ].push_back( new_sg );
1730 m_subgraphs.push_back( new_sg );
1731 new_subgraphs.push_back( new_sg );
1732 }
1733 }
1734 }
1735
1736 std::copy( new_subgraphs.begin(), new_subgraphs.end(),
1737 std::back_inserter( m_driver_subgraphs ) );
1738}
1739
1740
1742{
1743 // Generate subgraphs for global power pins. These will be merged with other subgraphs
1744 // on the same sheet in the next loop.
1745 // These are NOT limited to power symbols, we support legacy invisible + power-in pins
1746 // on non-power symbols.
1747
1748 std::unordered_map<int, CONNECTION_SUBGRAPH*> global_power_pin_subgraphs;
1749
1750 for( const auto& it : m_global_power_pins )
1751 {
1752 SCH_SHEET_PATH sheet = it.first;
1753 SCH_PIN* pin = it.second;
1754
1755 if( !pin->ConnectedItems( sheet ).empty()
1756 && !pin->GetLibPin()->GetParentSymbol()->IsGlobalPower() )
1757 {
1758 // ERC will warn about this: user has wired up an invisible pin
1759 continue;
1760 }
1761
1762 SCH_CONNECTION* connection = pin->GetOrInitConnection( sheet, this );
1763
1764 // If this pin already has a subgraph, don't need to process
1765 if( !connection || connection->SubgraphCode() > 0 )
1766 continue;
1767
1768 // Proper modern power symbols get their net name from the value field
1769 // in the symbol, but we support legacy non-power symbols with global
1770 // power connections based on invisible, power-in, pin's names.
1771 if( pin->GetLibPin()->GetParentSymbol()->IsGlobalPower() )
1772 connection->SetName( pin->GetParentSymbol()->GetValue( true, &sheet, false ) );
1773 else
1774 connection->SetName( pin->GetShownName() );
1775
1776 int code = assignNewNetCode( *connection );
1777
1778 connection->SetNetCode( code );
1779
1780 CONNECTION_SUBGRAPH* subgraph;
1781 auto jj = global_power_pin_subgraphs.find( code );
1782
1783 if( jj != global_power_pin_subgraphs.end() )
1784 {
1785 subgraph = jj->second;
1786 subgraph->AddItem( pin );
1787 }
1788 else
1789 {
1790 subgraph = new CONNECTION_SUBGRAPH( this );
1791
1792 subgraph->m_code = m_last_subgraph_code++;
1793 subgraph->m_sheet = sheet;
1794
1795 subgraph->AddItem( pin );
1796 subgraph->ResolveDrivers();
1797
1798 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(), code };
1799 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1800 m_subgraphs.push_back( subgraph );
1801 m_driver_subgraphs.push_back( subgraph );
1802
1803 global_power_pin_subgraphs[code] = subgraph;
1804 }
1805
1806 connection->SetSubgraphCode( subgraph->m_code );
1807 }
1808}
1809
1810
1812{
1813 // Here we do all the local (sheet) processing of each subgraph, including assigning net
1814 // codes, merging subgraphs together that use label connections, etc.
1815
1816 // Cache remaining valid subgraphs by sheet path
1817 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1818 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1819
1820 std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
1821
1822 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1823 {
1824 if( subgraph->m_absorbed )
1825 continue;
1826
1827 SCH_CONNECTION* connection = subgraph->m_driver_connection;
1828 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1829 wxString name = connection->Name();
1830
1831 // Test subgraphs with weak drivers for net name conflicts and fix them
1832 unsigned suffix = 1;
1833
1834 auto create_new_name =
1835 [&suffix]( SCH_CONNECTION* aConn ) -> wxString
1836 {
1837 wxString newName;
1838 wxString suffixStr = std::to_wstring( suffix );
1839
1840 // For group buses with a prefix, we can add the suffix to the prefix.
1841 // If they don't have a prefix, we force the creation of a prefix so that
1842 // two buses don't get inadvertently shorted together.
1843 if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
1844 {
1845 wxString prefix = aConn->BusPrefix();
1846
1847 if( prefix.empty() )
1848 prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
1849
1850 wxString oldName = aConn->Name().AfterFirst( '{' );
1851
1852 newName << prefix << wxT( "_" ) << suffixStr << wxT( "{" ) << oldName;
1853
1854 aConn->ConfigureFromLabel( newName );
1855 }
1856 else
1857 {
1858 newName << aConn->Name() << wxT( "_" ) << suffixStr;
1859 aConn->SetSuffix( wxString( wxT( "_" ) ) << suffixStr );
1860 }
1861
1862 suffix++;
1863 return newName;
1864 };
1865
1866 if( !subgraph->m_strong_driver )
1867 {
1868 std::vector<CONNECTION_SUBGRAPH*> vec_empty;
1869 std::vector<CONNECTION_SUBGRAPH*>* vec = &vec_empty;
1870
1871 if( m_net_name_to_subgraphs_map.count( name ) )
1872 vec = &m_net_name_to_subgraphs_map.at( name );
1873
1874 // If we are a unique bus vector, check if we aren't actually unique because of another
1875 // subgraph with a similar bus vector
1876 if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1877 {
1878 wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
1879
1880 if( m_net_name_to_subgraphs_map.count( prefixOnly ) )
1881 vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
1882 }
1883
1884 if( vec->size() > 1 )
1885 {
1886 wxString new_name = create_new_name( connection );
1887
1888 while( m_net_name_to_subgraphs_map.contains( new_name ) )
1889 new_name = create_new_name( connection );
1890
1891 wxLogTrace( ConnTrace,
1892 wxS( "%ld (%s) is weakly driven and not unique. Changing to %s." ),
1893 subgraph->m_code, name, new_name );
1894
1895 std::erase( *vec, subgraph );
1896
1897 m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1898
1899 name = new_name;
1900 }
1901 else if( subgraph->m_driver )
1902 {
1903 // If there is no conflict, promote sheet pins to be strong drivers so that they
1904 // will be considered below for propagation/merging.
1905
1906 // It is possible for this to generate a conflict if the sheet pin has the same
1907 // name as a global label on the same sheet, because global merging will then treat
1908 // this subgraph as if it had a matching local label. So, for those cases, we
1909 // don't apply this promotion
1910
1911 if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1912 {
1913 bool conflict = false;
1914 wxString global_name = connection->Name( true );
1915 auto kk = m_net_name_to_subgraphs_map.find( global_name );
1916
1917 if( kk != m_net_name_to_subgraphs_map.end() )
1918 {
1919 // A global will conflict if it is on the same sheet as this subgraph, since
1920 // it would be connected by implicit local label linking
1921 std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
1922
1923 for( const CONNECTION_SUBGRAPH* candidate : candidates )
1924 {
1925 if( candidate->m_sheet == sheet )
1926 conflict = true;
1927 }
1928 }
1929
1930 if( conflict )
1931 {
1932 wxLogTrace( ConnTrace,
1933 wxS( "%ld (%s) skipped for promotion due to potential "
1934 "conflict" ),
1935 subgraph->m_code, name );
1936 }
1937 else
1938 {
1939 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
1940
1941 wxLogTrace( ConnTrace,
1942 wxS( "%ld (%s) weakly driven by unique sheet pin %s, "
1943 "promoting" ),
1944 subgraph->m_code, name,
1945 subgraph->m_driver->GetItemDescription( &unitsProvider,
1946 true ) );
1947
1948 subgraph->m_strong_driver = true;
1949 }
1950 }
1951 }
1952 }
1953
1954 // Assign net codes
1955 if( connection->IsBus() )
1956 {
1957 int code = -1;
1958 auto it = m_bus_name_to_code_map.find( name );
1959
1960 if( it != m_bus_name_to_code_map.end() )
1961 {
1962 code = it->second;
1963 }
1964 else
1965 {
1966 code = m_last_bus_code++;
1967 m_bus_name_to_code_map[ name ] = code;
1968 }
1969
1970 connection->SetBusCode( code );
1971 assignNetCodesToBus( connection );
1972 }
1973 else
1974 {
1975 assignNewNetCode( *connection );
1976 }
1977
1978 // Reset the flag for the next loop below
1979 subgraph->m_dirty = true;
1980
1981 // Next, we merge together subgraphs that have label connections, and create
1982 // neighbor links for subgraphs that are part of a bus on the same sheet.
1983 // For merging, we consider each possible strong driver.
1984
1985 // If this subgraph doesn't have a strong driver, let's skip it, since there is no
1986 // way it will be merged with anything.
1987 if( !subgraph->m_strong_driver )
1988 continue;
1989
1990 // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
1991 // as the subgraph we are considering that has a strong driver.
1992 // Weakly driven subgraphs are not considered since they will never be absorbed or
1993 // form neighbor links.
1994 std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
1995 std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
1996 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
1997 std::back_inserter( candidate_subgraphs ),
1998 [&] ( const CONNECTION_SUBGRAPH* candidate )
1999 {
2000 return ( !candidate->m_absorbed &&
2001 candidate->m_strong_driver &&
2002 candidate != subgraph );
2003 } );
2004
2005 // This is a list of connections on the current subgraph to compare to the
2006 // drivers of each candidate subgraph. If the current subgraph is a bus,
2007 // we should consider each bus member.
2008 std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
2009
2010 // Also check the main driving connection
2011 connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
2012
2013 auto add_connections_to_check =
2014 [&] ( CONNECTION_SUBGRAPH* aSubgraph )
2015 {
2016 for( SCH_ITEM* possible_driver : aSubgraph->m_items )
2017 {
2018 if( possible_driver == aSubgraph->m_driver )
2019 continue;
2020
2021 auto c = getDefaultConnection( possible_driver, aSubgraph );
2022
2023 if( c )
2024 {
2025 if( c->Type() != aSubgraph->m_driver_connection->Type() )
2026 continue;
2027
2028 if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
2029 continue;
2030
2031 connections_to_check.push_back( c );
2032 wxLogTrace( ConnTrace,
2033 wxS( "%lu (%s): Adding secondary driver %s" ),
2034 aSubgraph->m_code,
2035 aSubgraph->m_driver_connection->Name( true ),
2036 c->Name( true ) );
2037 }
2038 }
2039 };
2040
2041 // Now add other strong drivers
2042 // The actual connection attached to these items will have been overwritten
2043 // by the chosen driver of the subgraph, so we need to create a dummy connection
2044 add_connections_to_check( subgraph );
2045
2046 std::set<SCH_CONNECTION*> checked_connections;
2047
2048 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2049 {
2050 auto member = connections_to_check[i];
2051
2052 // Don't check the same connection twice
2053 if( !checked_connections.insert( member.get() ).second )
2054 continue;
2055
2056 if( member->IsBus() )
2057 {
2058 connections_to_check.insert( connections_to_check.end(),
2059 member->Members().begin(),
2060 member->Members().end() );
2061 }
2062
2063 wxString test_name = member->Name( true );
2064
2065 for( CONNECTION_SUBGRAPH* candidate : candidate_subgraphs )
2066 {
2067 if( candidate->m_absorbed || candidate == subgraph )
2068 continue;
2069
2070 bool match = false;
2071
2072 if( candidate->m_driver_connection->Name( true ) == test_name )
2073 {
2074 match = true;
2075 }
2076 else
2077 {
2078 if( !candidate->m_multiple_drivers )
2079 continue;
2080
2081 for( SCH_ITEM *driver : candidate->m_drivers )
2082 {
2083 if( driver == candidate->m_driver )
2084 continue;
2085
2086 // Sheet pins are not candidates for merging
2087 if( driver->Type() == SCH_SHEET_PIN_T )
2088 continue;
2089
2090 if( driver->Type() == SCH_PIN_T )
2091 {
2092 auto pin = static_cast<SCH_PIN*>( driver );
2093
2094 if( pin->IsPower()
2095 && pin->GetDefaultNetName( sheet ) == test_name )
2096 {
2097 match = true;
2098 break;
2099 }
2100 }
2101 else
2102 {
2103 // Should we skip this if the driver type is not one of these types?
2104 wxASSERT( driver->Type() == SCH_LABEL_T ||
2105 driver->Type() == SCH_GLOBAL_LABEL_T ||
2106 driver->Type() == SCH_HIER_LABEL_T );
2107
2108 if( subgraph->GetNameForDriver( driver ) == test_name )
2109 {
2110 match = true;
2111 break;
2112 }
2113 }
2114 }
2115 }
2116
2117 if( match )
2118 {
2119 if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
2120 {
2121 wxLogTrace( ConnTrace, wxS( "%lu (%s) has bus child %lu (%s)" ),
2122 subgraph->m_code, connection->Name(),
2123 candidate->m_code, member->Name() );
2124
2125 subgraph->m_bus_neighbors[member].insert( candidate );
2126 candidate->m_bus_parents[member].insert( subgraph );
2127 }
2128 else if( !connection->IsBus()
2129 || connection->Type() == candidate->m_driver_connection->Type() )
2130 {
2131 wxLogTrace( ConnTrace, wxS( "%lu (%s) absorbs neighbor %lu (%s)" ),
2132 subgraph->m_code, connection->Name(),
2133 candidate->m_code, candidate->m_driver_connection->Name() );
2134
2135 // Candidate may have other non-chosen drivers we need to follow
2136 add_connections_to_check( candidate );
2137
2138 subgraph->Absorb( candidate );
2139 invalidated_subgraphs.insert( subgraph );
2140 }
2141 }
2142 }
2143 }
2144 }
2145
2146 // Update any subgraph that was invalidated above
2147 for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
2148 {
2149 if( subgraph->m_absorbed )
2150 continue;
2151
2152 if( !subgraph->ResolveDrivers() )
2153 continue;
2154
2155 if( subgraph->m_driver_connection->IsBus() )
2156 assignNetCodesToBus( subgraph->m_driver_connection );
2157 else
2158 assignNewNetCode( *subgraph->m_driver_connection );
2159
2160 wxLogTrace( ConnTrace, wxS( "Re-resolving drivers for %lu (%s)" ), subgraph->m_code,
2161 subgraph->m_driver_connection->Name() );
2162 }
2163
2164}
2165
2166
2167// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
2168// to the same subgraph necessarily if it runs over and over again on the same
2169// sheet. We need:
2170//
2171// a) a cache of net/bus codes, like used before
2172// b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
2173// c) some way of trying to avoid changing net names. so we should keep track
2174// of the previous driver of a net, and if it comes down to choosing between
2175// equally-prioritized drivers, choose the one that already exists as a driver
2176// on some portion of the items.
2177
2178
2179void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2180 bool aUnconditional )
2181{
2182 // Recache all bus aliases for later use
2183 wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) );
2184
2185 SCH_SCREENS screens( m_schematic->Root() );
2186
2187 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2188 {
2189 for( const std::shared_ptr<BUS_ALIAS>& alias : screen->GetBusAliases() )
2190 m_bus_alias_cache[alias->GetName()] = alias;
2191 }
2192
2193 PROF_TIMER sub_graph( "buildItemSubGraphs" );
2195
2196 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2197 sub_graph.Show();
2198
2205
2207
2209
2211
2212 PROF_TIMER proc_sub_graph( "ProcessSubGraphs" );
2214
2215 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2216 proc_sub_graph.Show();
2217
2218 // Absorbed subgraphs should no longer be considered
2219 std::erase_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool
2220 {
2221 return candidate->m_absorbed;
2222 } );
2223
2224 // Store global subgraphs for later reference
2225 std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
2226 std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
2227 std::back_inserter( global_subgraphs ),
2228 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
2229 {
2230 return !candidate->m_local_driver;
2231 } );
2232
2233 // Recache remaining valid subgraphs by sheet path
2235
2236 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2237 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
2238
2240
2241 auto results = tp.parallelize_loop( m_driver_subgraphs.size(),
2242 [&]( const int a, const int b)
2243 {
2244 for( int ii = a; ii < b; ++ii )
2245 m_driver_subgraphs[ii]->UpdateItemConnections();
2246 });
2247
2248 results.wait();
2249
2250 // Next time through the subgraphs, we do some post-processing to handle things like
2251 // connecting bus members to their neighboring subgraphs, and then propagate connections
2252 // through the hierarchy
2253 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2254 {
2255 if( !subgraph->m_dirty )
2256 continue;
2257
2258 wxLogTrace( ConnTrace, wxS( "Processing %lu (%s) for propagation" ), subgraph->m_code,
2259 subgraph->m_driver_connection->Name() );
2260
2261 // For subgraphs that are driven by a global (power port or label) and have more
2262 // than one global driver, we need to seek out other subgraphs driven by the
2263 // same name as the non-chosen driver and update them to match the chosen one.
2264
2265 if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
2266 {
2267 for( SCH_ITEM* driver : subgraph->m_drivers )
2268 {
2269 if( driver == subgraph->m_driver )
2270 continue;
2271
2272 const wxString& secondary_name = subgraph->GetNameForDriver( driver );
2273
2274 if( secondary_name == subgraph->m_driver_connection->Name() )
2275 continue;
2276
2277 bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
2279
2280 for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
2281 {
2282 if( candidate == subgraph )
2283 continue;
2284
2285 if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
2286 continue;
2287
2288 for( SCH_ITEM* candidate_driver : candidate->m_drivers )
2289 {
2290 if( candidate->GetNameForDriver( candidate_driver ) == secondary_name )
2291 {
2292 wxLogTrace( ConnTrace, wxS( "Global %lu (%s) promoted to %s" ),
2293 candidate->m_code, candidate->m_driver_connection->Name(),
2294 subgraph->m_driver_connection->Name() );
2295
2296 candidate->m_driver_connection->Clone( *subgraph->m_driver_connection );
2297
2298 candidate->m_dirty = false;
2299 propagateToNeighbors( candidate, false );
2300 }
2301 }
2302 }
2303 }
2304 }
2305
2306 // This call will handle descending the hierarchy and updating child subgraphs
2307 propagateToNeighbors( subgraph, false );
2308 }
2309
2310 // After processing and allowing some to be skipped if they have hierarchical
2311 // pins connecting both up and down the hierarchy, we check to see if any of them
2312 // have not been processed. This would indicate that they do not have off-sheet connections
2313 // but we still need to handle the subgraph
2314 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2315 {
2316 if( subgraph->m_dirty )
2317 propagateToNeighbors( subgraph, true );
2318 }
2319
2320 // Handle buses that have been linked together somewhere by member (net) connections.
2321 // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
2322
2323 // For net subgraphs that have more than one bus parent, we need to ensure that those
2324 // buses are linked together in the final netlist. The final name of each bus might not
2325 // match the local name that was used to establish the parent-child relationship, because
2326 // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
2327 // we need to identify the appropriate bus members to link together (and their final names),
2328 // and then update all instances of the old name in the hierarchy.
2329 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2330 {
2331 // All SGs should have been processed by propagateToNeighbors above
2332 // Should we skip all of this if the subgraph is not dirty?
2333 wxASSERT_MSG( !subgraph->m_dirty,
2334 wxS( "Subgraph not processed by propagateToNeighbors!" ) );
2335
2336 if( subgraph->m_bus_parents.size() < 2 )
2337 continue;
2338
2339 SCH_CONNECTION* conn = subgraph->m_driver_connection;
2340
2341 wxLogTrace( ConnTrace, wxS( "%lu (%s) has multiple bus parents" ),
2342 subgraph->m_code, conn->Name() );
2343
2344 // Should we skip everything after this if this is not a net?
2345 wxCHECK2( conn->IsNet(), continue );
2346
2347 for( const auto& ii : subgraph->m_bus_parents )
2348 {
2349 SCH_CONNECTION* link_member = ii.first.get();
2350
2351 for( CONNECTION_SUBGRAPH* parent : ii.second )
2352 {
2353 while( parent->m_absorbed )
2354 parent = parent->m_absorbed_by;
2355
2356 SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
2357
2358 if( !match )
2359 {
2360 wxLogTrace( ConnTrace, wxS( "Warning: could not match %s inside %lu (%s)" ),
2361 conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
2362 continue;
2363 }
2364
2365 if( conn->Name() != match->Name() )
2366 {
2367 wxString old_name = match->Name();
2368
2369 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ),
2370 parent->m_code, parent->m_driver_connection->Name(),
2371 old_name, conn->Name() );
2372
2373 match->Clone( *conn );
2374
2375 auto jj = m_net_name_to_subgraphs_map.find( old_name );
2376
2377 if( jj == m_net_name_to_subgraphs_map.end() )
2378 continue;
2379
2380 for( CONNECTION_SUBGRAPH* old_sg : jj->second )
2381 {
2382 while( old_sg->m_absorbed )
2383 old_sg = old_sg->m_absorbed_by;
2384
2385 old_sg->m_driver_connection->Clone( *conn );
2386 }
2387 }
2388 }
2389 }
2390 }
2391
2392 auto updateItemConnectionsTask =
2393 [&]( CONNECTION_SUBGRAPH* subgraph ) -> size_t
2394 {
2395 // Make sure weakly-driven single-pin nets get the unconnected_ prefix
2396 if( !subgraph->m_strong_driver && subgraph->m_drivers.size() == 1 &&
2397 subgraph->m_driver->Type() == SCH_PIN_T )
2398 {
2399 SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
2400 wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
2401
2402 subgraph->m_driver_connection->ConfigureFromLabel( name );
2403 }
2404
2405 subgraph->m_dirty = false;
2406 subgraph->UpdateItemConnections();
2407
2408 // No other processing to do on buses
2409 if( subgraph->m_driver_connection->IsBus() )
2410 return 0;
2411
2412 // As a visual aid, we can check sheet pins that are driven by themselves to see
2413 // if they should be promoted to buses
2414 if( subgraph->m_driver && subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
2415 {
2416 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
2417
2418 if( SCH_SHEET* sheet = pin->GetParent() )
2419 {
2420 wxString pinText = pin->GetShownText( false );
2421 SCH_SCREEN* screen = sheet->GetScreen();
2422
2423 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
2424 {
2425 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
2426
2427 if( label->GetShownText( &subgraph->m_sheet, false ) == pinText )
2428 {
2429 SCH_SHEET_PATH path = subgraph->m_sheet;
2430 path.push_back( sheet );
2431
2432 SCH_CONNECTION* parent_conn = label->Connection( &path );
2433
2434 if( parent_conn && parent_conn->IsBus() )
2435 subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
2436
2437 break;
2438 }
2439 }
2440
2441 if( subgraph->m_driver_connection->IsBus() )
2442 return 0;
2443 }
2444 }
2445
2446 return 1;
2447 };
2448
2449 auto results2 = tp.parallelize_loop( m_driver_subgraphs.size(),
2450 [&]( const int a, const int b)
2451 {
2452 for( int ii = a; ii < b; ++ii )
2453 updateItemConnectionsTask( m_driver_subgraphs[ii] );
2454 });
2455 results2.wait();
2456
2459
2460 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2461 {
2462 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(),
2463 subgraph->m_driver_connection->NetCode() };
2464 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
2465
2466 m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
2467 }
2468
2469 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Project().GetProjectFile().m_NetSettings;
2470 std::map<wxString, std::set<wxString>> oldAssignments =
2471 netSettings->GetNetclassLabelAssignments();
2472 std::set<wxString> affectedNetclassNetAssignments;
2473
2474 netSettings->ClearNetclassLabelAssignments();
2475
2476 auto dirtySubgraphs =
2477 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2478 {
2479 if( aChangedItemHandler )
2480 {
2481 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2482 {
2483 for( SCH_ITEM* item : subgraph->m_items )
2484 (*aChangedItemHandler)( item );
2485 }
2486 }
2487 };
2488
2489 auto checkNetclassDrivers =
2490 [&]( const wxString& netName, const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2491 {
2492 wxCHECK_RET( !subgraphs.empty(), wxS( "Invalid empty subgraph" ) );
2493
2494 std::set<wxString> netclasses;
2495
2496 // Collect all netclasses on all subgraphs for this net
2497 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2498 {
2499 for( SCH_ITEM* item : subgraph->m_items )
2500 {
2501 std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProviders =
2502 subgraph->GetNetclassesForDriver( item );
2503
2504 for( std::pair<wxString, SCH_ITEM*>& ncPair : netclassesWithProviders )
2505 netclasses.insert( std::move( ncPair.first ) );
2506 }
2507 }
2508
2509 // Append the netclasses to any included bus members
2510 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2511 {
2512 if( subgraph->m_driver_connection->IsBus() )
2513 {
2514 auto processBusMember = [&, this]( const SCH_CONNECTION* member )
2515 {
2516 if( !netclasses.empty() )
2517 {
2518 netSettings->AppendNetclassLabelAssignment( member->Name(), netclasses );
2519 }
2520
2521 auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
2522
2523 if( oldAssignments.count( member->Name() ) )
2524 {
2525 if( oldAssignments[member->Name()] != netclasses )
2526 {
2527 affectedNetclassNetAssignments.insert( member->Name() );
2528
2529 if( ii != m_net_name_to_subgraphs_map.end() )
2530 dirtySubgraphs( ii->second );
2531 }
2532 }
2533 else if( !netclasses.empty() )
2534 {
2535 affectedNetclassNetAssignments.insert( member->Name() );
2536
2537 if( ii != m_net_name_to_subgraphs_map.end() )
2538 dirtySubgraphs( ii->second );
2539 }
2540 };
2541
2542 for( const std::shared_ptr<SCH_CONNECTION>& member :
2543 subgraph->m_driver_connection->Members() )
2544 {
2545 // Check if this member itself is a bus (which can be the case
2546 // for vector buses as members of a bus, see
2547 // https://gitlab.com/kicad/code/kicad/-/issues/16545
2548 if( member->IsBus() )
2549 {
2550 for( const std::shared_ptr<SCH_CONNECTION>& nestedMember :
2551 member->Members() )
2552 {
2553 processBusMember( nestedMember.get() );
2554 }
2555 }
2556 else
2557 {
2558 processBusMember( member.get() );
2559 }
2560 }
2561 }
2562 }
2563
2564 // Assign the netclasses to the root netname
2565 if( !netclasses.empty() )
2566 {
2567 netSettings->AppendNetclassLabelAssignment( netName, netclasses );
2568 }
2569
2570 if( oldAssignments.count( netName ) )
2571 {
2572 if( oldAssignments[netName] != netclasses )
2573 {
2574 affectedNetclassNetAssignments.insert( netName );
2575 dirtySubgraphs( subgraphs );
2576 }
2577 }
2578 else if( !netclasses.empty() )
2579 {
2580 affectedNetclassNetAssignments.insert( netName );
2581 dirtySubgraphs( subgraphs );
2582 }
2583 };
2584
2585 // Check for netclass assignments
2586 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
2587 checkNetclassDrivers( netname, subgraphs );
2588
2589 if( !aUnconditional )
2590 {
2591 for( auto& [netname, netclasses] : oldAssignments )
2592 {
2593 if( netSettings->GetNetclassLabelAssignments().count( netname )
2594 || affectedNetclassNetAssignments.count( netname ) )
2595 {
2596 continue;
2597 }
2598
2599 netSettings->SetNetclassLabelAssignment( netname, netclasses );
2600 }
2601 }
2602}
2603
2604
2605int CONNECTION_GRAPH::getOrCreateNetCode( const wxString& aNetName )
2606{
2607 int code;
2608
2609 auto it = m_net_name_to_code_map.find( aNetName );
2610
2611 if( it == m_net_name_to_code_map.end() )
2612 {
2613 code = m_last_net_code++;
2614 m_net_name_to_code_map[ aNetName ] = code;
2615 }
2616 else
2617 {
2618 code = it->second;
2619 }
2620
2621 return code;
2622}
2623
2624
2626{
2627 int code = getOrCreateNetCode( aConnection.Name() );
2628
2629 aConnection.SetNetCode( code );
2630
2631 return code;
2632}
2633
2634
2636{
2637 std::vector<std::shared_ptr<SCH_CONNECTION>> connections_to_check( aConnection->Members() );
2638
2639 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2640 {
2641 const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
2642
2643 if( member->IsBus() )
2644 {
2645 connections_to_check.insert( connections_to_check.end(),
2646 member->Members().begin(),
2647 member->Members().end() );
2648 continue;
2649 }
2650
2651 assignNewNetCode( *member );
2652 }
2653}
2654
2655
2657{
2658 SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
2659 std::vector<CONNECTION_SUBGRAPH*> search_list;
2660 std::unordered_set<CONNECTION_SUBGRAPH*> visited;
2661 std::unordered_set<SCH_CONNECTION*> stale_bus_members;
2662
2663 auto visit =[&]( CONNECTION_SUBGRAPH* aParent )
2664 {
2665 for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
2666 {
2667 SCH_SHEET_PATH path = aParent->m_sheet;
2668 path.push_back( pin->GetParent() );
2669
2670 auto it = m_sheet_to_subgraphs_map.find( path );
2671
2672 if( it == m_sheet_to_subgraphs_map.end() )
2673 continue;
2674
2675 for( CONNECTION_SUBGRAPH* candidate : it->second )
2676 {
2677 if( !candidate->m_strong_driver
2678 || candidate->m_hier_ports.empty()
2679 || visited.contains( candidate ) )
2680 {
2681 continue;
2682 }
2683
2684 for( SCH_HIERLABEL* label : candidate->m_hier_ports )
2685 {
2686 if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
2687 {
2688 wxLogTrace( ConnTrace, wxS( "%lu: found child %lu (%s)" ), aParent->m_code,
2689 candidate->m_code, candidate->m_driver_connection->Name() );
2690
2691 candidate->m_hier_parent = aParent;
2692 aParent->m_hier_children.insert( candidate );
2693
2694 // Should we skip adding the candidate to the list if the parent and candidate subgraphs
2695 // are not the same?
2696 wxASSERT( candidate->m_graph == aParent->m_graph );
2697
2698 search_list.push_back( candidate );
2699 break;
2700 }
2701 }
2702 }
2703 }
2704
2705 for( SCH_HIERLABEL* label : aParent->m_hier_ports )
2706 {
2707 SCH_SHEET_PATH path = aParent->m_sheet;
2708 path.pop_back();
2709
2710 auto it = m_sheet_to_subgraphs_map.find( path );
2711
2712 if( it == m_sheet_to_subgraphs_map.end() )
2713 continue;
2714
2715 for( CONNECTION_SUBGRAPH* candidate : it->second )
2716 {
2717 if( candidate->m_hier_pins.empty()
2718 || visited.contains( candidate )
2719 || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
2720 {
2721 continue;
2722 }
2723
2724 const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
2725
2726 for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
2727 {
2728 // If the last sheet UUIDs won't match, no need to check the full path
2729 if( pin->GetParent()->m_Uuid != last_parent_uuid )
2730 continue;
2731
2732 SCH_SHEET_PATH pin_path = path;
2733 pin_path.push_back( pin->GetParent() );
2734
2735 if( pin_path != aParent->m_sheet )
2736 continue;
2737
2738 if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
2739 {
2740 wxLogTrace( ConnTrace, wxS( "%lu: found additional parent %lu (%s)" ),
2741 aParent->m_code, candidate->m_code,
2742 candidate->m_driver_connection->Name() );
2743
2744 aParent->m_hier_children.insert( candidate );
2745 search_list.push_back( candidate );
2746 break;
2747 }
2748 }
2749 }
2750 }
2751 };
2752
2753 auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph )
2754 {
2755 for( const auto& kv : aParentGraph->m_bus_neighbors )
2756 {
2757 for( CONNECTION_SUBGRAPH* neighbor : kv.second )
2758 {
2759 // May have been absorbed but won't have been deleted
2760 while( neighbor->m_absorbed )
2761 neighbor = neighbor->m_absorbed_by;
2762
2763 SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
2764
2765 // Now member may be out of date, since we just cloned the
2766 // connection from higher up in the hierarchy. We need to
2767 // figure out what the actual new connection is.
2768 SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
2769
2770 if( !member )
2771 {
2772 // Try harder: we might match on a secondary driver
2773 for( CONNECTION_SUBGRAPH* sg : kv.second )
2774 {
2775 if( sg->m_multiple_drivers )
2776 {
2777 SCH_SHEET_PATH sheet = sg->m_sheet;
2778
2779 for( SCH_ITEM* driver : sg->m_drivers )
2780 {
2781 auto c = getDefaultConnection( driver, sg );
2782 member = matchBusMember( parent, c.get() );
2783
2784 if( member )
2785 break;
2786 }
2787 }
2788
2789 if( member )
2790 break;
2791 }
2792 }
2793
2794 // This is bad, probably an ERC error
2795 if( !member )
2796 {
2797 wxLogTrace( ConnTrace, wxS( "Could not match bus member %s in %s" ),
2798 kv.first->Name(), parent->Name() );
2799 continue;
2800 }
2801
2802 SCH_CONNECTION* neighbor_conn = neighbor->m_driver_connection;
2803
2804 wxCHECK2( neighbor_conn, continue );
2805
2806 wxString neighbor_name = neighbor_conn->Name();
2807
2808 // Matching name: no update needed
2809 if( neighbor_name == member->Name() )
2810 continue;
2811
2812 // Was this neighbor already updated from a different sheet? Don't rename it again
2813 if( neighbor_conn->Sheet() != neighbor->m_sheet )
2814 continue;
2815
2816 // Safety check against infinite recursion
2817 wxCHECK2_MSG( neighbor_conn->IsNet(), continue,
2818 wxS( "\"" ) + neighbor_name + wxS( "\" is not a net." ) );
2819
2820 wxLogTrace( ConnTrace, wxS( "%lu (%s) connected to bus member %s (local %s)" ),
2821 neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
2822
2823 // Take whichever name is higher priority
2826 {
2827 member->Clone( *neighbor_conn );
2828 stale_bus_members.insert( member );
2829 }
2830 else
2831 {
2832 neighbor_conn->Clone( *member );
2833
2834 recacheSubgraphName( neighbor, neighbor_name );
2835
2836 // Recurse onto this neighbor in case it needs to re-propagate
2837 neighbor->m_dirty = true;
2838 propagateToNeighbors( neighbor, aForce );
2839 }
2840 }
2841 }
2842 };
2843
2844 // If we are a bus, we must propagate to local neighbors and then the hierarchy
2845 if( conn->IsBus() )
2846 propagate_bus_neighbors( aSubgraph );
2847
2848 // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
2849 // If we only have one or the other, process (we can either go bottom-up or top-down depending
2850 // on which subgraph comes up first)
2851 if( !aForce && !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
2852 {
2853 wxLogTrace( ConnTrace, wxS( "%lu (%s) has both hier ports and pins; deferring processing" ),
2854 aSubgraph->m_code, conn->Name() );
2855 return;
2856 }
2857 else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
2858 {
2859 wxLogTrace( ConnTrace,
2860 wxS( "%lu (%s) has no hier pins or ports on sheet %s; marking clean" ),
2861 aSubgraph->m_code, conn->Name(), aSubgraph->m_sheet.PathHumanReadable() );
2862 aSubgraph->m_dirty = false;
2863 return;
2864 }
2865
2866 visited.insert( aSubgraph );
2867
2868 wxLogTrace( ConnTrace, wxS( "Propagating %lu (%s) to subsheets" ),
2869 aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
2870
2871 visit( aSubgraph );
2872
2873 for( unsigned i = 0; i < search_list.size(); i++ )
2874 {
2875 auto child = search_list[i];
2876
2877 if( visited.insert( child ).second )
2878 visit( child );
2879
2880 child->m_dirty = false;
2881 }
2882
2883 // Now, find the best driver for this chain of subgraphs
2884 CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
2887 bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2888 wxString bestName = aSubgraph->m_driver_connection->Name();
2889
2890 // Check if a subsheet has a higher-priority connection to the same net
2892 {
2893 for( CONNECTION_SUBGRAPH* subgraph : visited )
2894 {
2895 if( subgraph == aSubgraph )
2896 continue;
2897
2899 CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
2900
2901 bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2902 wxString candidateName = subgraph->m_driver_connection->Name();
2903 bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
2904 bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
2905
2906 // Pick a better driving subgraph if it:
2907 // a) has a power pin or global driver
2908 // b) is a strong driver and we're a weak driver
2909 // c) is a higher priority strong driver
2910 // d) matches our priority, is a strong driver, and has a shorter path
2911 // e) matches our strength and is at least as short, and is alphabetically lower
2912
2914 ( !bestIsStrong && candidateStrong ) ||
2915 ( priority > highest && candidateStrong ) ||
2916 ( priority == highest && candidateStrong && shorterPath ) ||
2917 ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
2918 ( candidateName < bestName ) ) )
2919 {
2920 bestDriver = subgraph;
2921 highest = priority;
2922 bestIsStrong = candidateStrong;
2923 bestName = candidateName;
2924 }
2925 }
2926 }
2927
2928 if( bestDriver != aSubgraph )
2929 {
2930 wxLogTrace( ConnTrace, wxS( "%lu (%s) overridden by new driver %lu (%s)" ),
2931 aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
2932 bestDriver->m_driver_connection->Name() );
2933 }
2934
2935 conn = bestDriver->m_driver_connection;
2936
2937 for( CONNECTION_SUBGRAPH* subgraph : visited )
2938 {
2939 wxString old_name = subgraph->m_driver_connection->Name();
2940
2941 subgraph->m_driver_connection->Clone( *conn );
2942
2943 if( old_name != conn->Name() )
2944 recacheSubgraphName( subgraph, old_name );
2945
2946 if( conn->IsBus() )
2947 propagate_bus_neighbors( subgraph );
2948 }
2949
2950 // Somewhere along the way, a bus member may have been upgraded to a global or power label.
2951 // Because this can happen anywhere, we need a second pass to update all instances of that bus
2952 // member to have the correct connection info
2953 if( conn->IsBus() && !stale_bus_members.empty() )
2954 {
2955 std::unordered_set<SCH_CONNECTION*> cached_members = stale_bus_members;
2956
2957 for( SCH_CONNECTION* stale_member : cached_members )
2958 {
2959 for( CONNECTION_SUBGRAPH* subgraph : visited )
2960 {
2961 SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
2962 stale_member );
2963
2964 if( !member )
2965 {
2966 wxLogTrace( ConnTrace, wxS( "WARNING: failed to match stale member %s in %s." ),
2967 stale_member->Name(), subgraph->m_driver_connection->Name() );
2968 continue;
2969 }
2970
2971 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ), subgraph->m_code,
2972 subgraph->m_driver_connection->Name(), member->LocalName(),
2973 stale_member->Name() );
2974
2975 member->Clone( *stale_member );
2976
2977 propagate_bus_neighbors( subgraph );
2978 }
2979 }
2980 }
2981
2982 aSubgraph->m_dirty = false;
2983}
2984
2985
2986std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
2987 CONNECTION_SUBGRAPH* aSubgraph )
2988{
2989 std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
2990
2991 switch( aItem->Type() )
2992 {
2993 case SCH_PIN_T:
2994 {
2995 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
2996
2997 if( pin->IsPower() )
2998 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2999
3000 break;
3001 }
3002
3003 case SCH_GLOBAL_LABEL_T:
3004 case SCH_HIER_LABEL_T:
3005 case SCH_LABEL_T:
3006 {
3007 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
3008 break;
3009 }
3010
3011 default:
3012 break;
3013 }
3014
3015 if( c )
3016 {
3017 c->SetGraph( this );
3018 c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
3019 }
3020
3021 return c;
3022}
3023
3024
3026 SCH_CONNECTION* aSearch )
3027{
3028 // Should we return a null pointer if the connection is not a bus connection?
3029 wxASSERT( aBusConnection->IsBus() );
3030
3031 SCH_CONNECTION* match = nullptr;
3032
3033 if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
3034 {
3035 // Vector bus: compare against index, because we allow the name
3036 // to be different
3037
3038 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
3039 {
3040 if( bus_member->VectorIndex() == aSearch->VectorIndex() )
3041 {
3042 match = bus_member.get();
3043 break;
3044 }
3045 }
3046 }
3047 else
3048 {
3049 // Group bus
3050 for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
3051 {
3052 // Vector inside group: compare names, because for bus groups
3053 // we expect the naming to be consistent across all usages
3054 // TODO(JE) explain this in the docs
3055 if( c->Type() == CONNECTION_TYPE::BUS )
3056 {
3057 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
3058 {
3059 if( bus_member->LocalName() == aSearch->LocalName() )
3060 {
3061 match = bus_member.get();
3062 break;
3063 }
3064 }
3065 }
3066 else if( c->LocalName() == aSearch->LocalName() )
3067 {
3068 match = c.get();
3069 break;
3070 }
3071 }
3072 }
3073
3074 return match;
3075}
3076
3077
3079 const wxString& aOldName )
3080{
3081 auto it = m_net_name_to_subgraphs_map.find( aOldName );
3082
3083 if( it != m_net_name_to_subgraphs_map.end() )
3084 {
3085 std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
3086 std::erase( vec, aSubgraph );
3087 }
3088
3089 wxLogTrace( ConnTrace, wxS( "recacheSubgraphName: %s => %s" ), aOldName,
3090 aSubgraph->m_driver_connection->Name() );
3091
3092 m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
3093}
3094
3095
3096std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
3097{
3098 auto it = m_bus_alias_cache.find( aName );
3099
3100 return it != m_bus_alias_cache.end() ? it->second : nullptr;
3101}
3102
3103
3104std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
3105{
3106 std::vector<const CONNECTION_SUBGRAPH*> ret;
3107
3108 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3109 {
3110 // Graph is supposed to be up-to-date before calling this
3111 // Should we continue if the subgraph is not up to date?
3112 wxASSERT( !subgraph->m_dirty );
3113
3114 if( !subgraph->m_driver )
3115 continue;
3116
3117 SCH_SHEET_PATH* sheet = &subgraph->m_sheet;
3118 SCH_CONNECTION* connection = subgraph->m_driver->Connection( sheet );
3119
3120 if( !connection->IsBus() )
3121 continue;
3122
3123 auto labels = subgraph->GetVectorBusLabels();
3124
3125 if( labels.size() > 1 )
3126 {
3127 bool different = false;
3128 wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText( sheet, false );
3129
3130 for( unsigned i = 1; i < labels.size(); ++i )
3131 {
3132 if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText( sheet,
3133 false ) != first )
3134 {
3135 different = true;
3136 break;
3137 }
3138 }
3139
3140 if( !different )
3141 continue;
3142
3143 wxLogTrace( ConnTrace, wxS( "SG %ld (%s) has multiple bus labels" ), subgraph->m_code,
3144 connection->Name() );
3145
3146 ret.push_back( subgraph );
3147 }
3148 }
3149
3150 return ret;
3151}
3152
3153
3155{
3156 wxString retval = aSubGraph->GetNetName();
3157 bool found = false;
3158
3159 // This is a hacky way to find the true subgraph net name (why do we not store it?)
3160 // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
3161
3162 for( auto it = m_net_name_to_subgraphs_map.begin();
3163 it != m_net_name_to_subgraphs_map.end() && !found; ++it )
3164 {
3165 for( CONNECTION_SUBGRAPH* graph : it->second )
3166 {
3167 if( graph == aSubGraph )
3168 {
3169 retval = it->first;
3170 found = true;
3171 break;
3172 }
3173 }
3174 }
3175
3176 return retval;
3177}
3178
3179
3181 const SCH_SHEET_PATH& aPath )
3182{
3183 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3184
3185 if( it == m_net_name_to_subgraphs_map.end() )
3186 return nullptr;
3187
3188 for( CONNECTION_SUBGRAPH* sg : it->second )
3189 {
3190 // Cache is supposed to be valid by now
3191 // Should we continue if the cache is not valid?
3192 wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
3193
3194 if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
3195 return sg;
3196 }
3197
3198 return nullptr;
3199}
3200
3201
3203{
3204 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3205
3206 if( it == m_net_name_to_subgraphs_map.end() )
3207 return nullptr;
3208
3209 // Should this return a nullptr if the map entry is empty?
3210 wxASSERT( !it->second.empty() );
3211
3212 return it->second[0];
3213}
3214
3215
3217{
3218 auto it = m_item_to_subgraph_map.find( aItem );
3219 CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
3220
3221 while( ret && ret->m_absorbed )
3222 ret = ret->m_absorbed_by;
3223
3224 return ret;
3225}
3226
3227
3228const std::vector<CONNECTION_SUBGRAPH*>&
3229CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
3230{
3231 static const std::vector<CONNECTION_SUBGRAPH*> subgraphs;
3232
3233 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3234
3235 if( it == m_net_name_to_subgraphs_map.end() )
3236 return subgraphs;
3237
3238 return it->second;
3239}
3240
3241
3243{
3244 int error_count = 0;
3245
3246 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::RunERC" ) );
3247
3248 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3249
3250 // We don't want to run many ERC checks more than once on a given screen even though it may
3251 // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
3252 std::set<SCH_ITEM*> seenDriverInstances;
3253
3254 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3255 {
3256 // There shouldn't be any null sub-graph pointers.
3257 wxCHECK2( subgraph, continue );
3258
3259 // Graph is supposed to be up-to-date before calling RunERC()
3260 // Should we continue if the subgraph is not up to date?
3261 wxASSERT( !subgraph->m_dirty );
3262
3263 if( subgraph->m_absorbed )
3264 continue;
3265
3266 if( seenDriverInstances.count( subgraph->m_driver ) )
3267 continue;
3268
3269 if( subgraph->m_driver )
3270 seenDriverInstances.insert( subgraph->m_driver );
3271
3282 if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
3283 {
3284 if( !ercCheckMultipleDrivers( subgraph ) )
3285 error_count++;
3286 }
3287
3288 subgraph->ResolveDrivers( false );
3289
3290 if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
3291 {
3292 if( !ercCheckBusToNetConflicts( subgraph ) )
3293 error_count++;
3294 }
3295
3296 if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
3297 {
3298 if( !ercCheckBusToBusEntryConflicts( subgraph ) )
3299 error_count++;
3300 }
3301
3302 if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
3303 {
3304 if( !ercCheckBusToBusConflicts( subgraph ) )
3305 error_count++;
3306 }
3307
3308 if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
3309 {
3310 if( !ercCheckFloatingWires( subgraph ) )
3311 error_count++;
3312 }
3313
3315 {
3316 if( !ercCheckDanglingWireEndpoints( subgraph ) )
3317 error_count++;
3318 }
3319
3322 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3323 {
3324 if( !ercCheckNoConnects( subgraph ) )
3325 error_count++;
3326 }
3327
3329 || settings.IsTestEnabled( ERCE_LABEL_SINGLE_PIN ) )
3330 {
3331 if( !ercCheckLabels( subgraph ) )
3332 error_count++;
3333 }
3334 }
3335
3336 if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
3337 {
3338 error_count += ercCheckDirectiveLabels();
3339 }
3340
3341 // Hierarchical sheet checking is done at the schematic level
3343 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3344 {
3345 error_count += ercCheckHierSheets();
3346 }
3347
3348 if( settings.IsTestEnabled( ERCE_SINGLE_GLOBAL_LABEL ) )
3349 {
3350 error_count += ercCheckSingleGlobalLabel();
3351 }
3352
3353 return error_count;
3354}
3355
3356
3358{
3359 wxCHECK( aSubgraph, false );
3360
3361 if( aSubgraph->m_multiple_drivers )
3362 {
3363 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3364 {
3365 if( driver == aSubgraph->m_driver )
3366 continue;
3367
3368 if( driver->Type() == SCH_GLOBAL_LABEL_T
3369 || driver->Type() == SCH_HIER_LABEL_T
3370 || driver->Type() == SCH_LABEL_T
3371 || ( driver->Type() == SCH_PIN_T
3372 && static_cast<SCH_PIN*>( driver )->IsPower() ) )
3373 {
3374 const wxString& primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
3375 const wxString& secondaryName = aSubgraph->GetNameForDriver( driver );
3376
3377 if( primaryName == secondaryName )
3378 continue;
3379
3380 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
3381 "items; %s will be used in the netlist" ),
3382 primaryName, secondaryName, primaryName );
3383
3384 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
3385 ercItem->SetItems( aSubgraph->m_driver, driver );
3386 ercItem->SetSheetSpecificPath( aSubgraph->GetSheet() );
3387 ercItem->SetItemsSheetPaths( aSubgraph->GetSheet(), aSubgraph->m_sheet );
3388 ercItem->SetErrorMessage( msg );
3389
3390 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), driver->GetPosition() );
3391 aSubgraph->m_sheet.LastScreen()->Append( marker );
3392
3393 return false;
3394 }
3395 }
3396 }
3397
3398 return true;
3399}
3400
3401
3403{
3404 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3405 SCH_SCREEN* screen = sheet.LastScreen();
3406
3407 SCH_ITEM* net_item = nullptr;
3408 SCH_ITEM* bus_item = nullptr;
3409 SCH_CONNECTION conn( this );
3410
3411 for( SCH_ITEM* item : aSubgraph->m_items )
3412 {
3413 switch( item->Type() )
3414 {
3415 case SCH_LINE_T:
3416 {
3417 if( item->GetLayer() == LAYER_BUS )
3418 bus_item = ( !bus_item ) ? item : bus_item;
3419 else
3420 net_item = ( !net_item ) ? item : net_item;
3421
3422 break;
3423 }
3424
3425 case SCH_LABEL_T:
3426 case SCH_GLOBAL_LABEL_T:
3427 case SCH_SHEET_PIN_T:
3428 case SCH_HIER_LABEL_T:
3429 {
3430 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3431 conn.ConfigureFromLabel( EscapeString( text->GetShownText( &sheet, false ),
3432 CTX_NETNAME ) );
3433
3434 if( conn.IsBus() )
3435 bus_item = ( !bus_item ) ? item : bus_item;
3436 else
3437 net_item = ( !net_item ) ? item : net_item;
3438
3439 break;
3440 }
3441
3442 default:
3443 break;
3444 }
3445 }
3446
3447 if( net_item && bus_item )
3448 {
3449 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
3450 ercItem->SetSheetSpecificPath( sheet );
3451 ercItem->SetItems( net_item, bus_item );
3452
3453 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), net_item->GetPosition() );
3454 screen->Append( marker );
3455
3456 return false;
3457 }
3458
3459 return true;
3460}
3461
3462
3464{
3465 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3466 SCH_SCREEN* screen = sheet.LastScreen();
3467
3468 SCH_ITEM* label = nullptr;
3469 SCH_ITEM* port = nullptr;
3470
3471 for( SCH_ITEM* item : aSubgraph->m_items )
3472 {
3473 switch( item->Type() )
3474 {
3475 case SCH_TEXT_T:
3476 case SCH_GLOBAL_LABEL_T:
3477 {
3478 if( !label && item->Connection( &sheet )->IsBus() )
3479 label = item;
3480 break;
3481 }
3482
3483 case SCH_SHEET_PIN_T:
3484 case SCH_HIER_LABEL_T:
3485 {
3486 if( !port && item->Connection( &sheet )->IsBus() )
3487 port = item;
3488 break;
3489 }
3490
3491 default:
3492 break;
3493 }
3494 }
3495
3496 if( label && port )
3497 {
3498 bool match = false;
3499
3500 for( const auto& member : label->Connection( &sheet )->Members() )
3501 {
3502 for( const auto& test : port->Connection( &sheet )->Members() )
3503 {
3504 if( test != member && member->Name() == test->Name() )
3505 {
3506 match = true;
3507 break;
3508 }
3509 }
3510
3511 if( match )
3512 break;
3513 }
3514
3515 if( !match )
3516 {
3517 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
3518 ercItem->SetSheetSpecificPath( sheet );
3519 ercItem->SetItems( label, port );
3520
3521 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), label->GetPosition() );
3522 screen->Append( marker );
3523
3524 return false;
3525 }
3526 }
3527
3528 return true;
3529}
3530
3531
3533{
3534 bool conflict = false;
3535 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3536 SCH_SCREEN* screen = sheet.LastScreen();
3537
3538 SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
3539 SCH_ITEM* bus_wire = nullptr;
3540 wxString bus_name;
3541
3542 if( !aSubgraph->m_driver_connection )
3543 {
3544 // Incomplete bus entry. Let the unconnected tests handle it.
3545 return true;
3546 }
3547
3548 for( SCH_ITEM* item : aSubgraph->m_items )
3549 {
3550 switch( item->Type() )
3551 {
3553 {
3554 if( !bus_entry )
3555 bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3556 break;
3557 }
3558
3559 default:
3560 break;
3561 }
3562 }
3563
3564 if( bus_entry && bus_entry->m_connected_bus_item )
3565 {
3566 bus_wire = bus_entry->m_connected_bus_item;
3567
3568 // Should we continue if the type is not a line?
3569 wxASSERT( bus_wire->Type() == SCH_LINE_T );
3570
3571 // In some cases, the connection list (SCH_CONNECTION*) can be null.
3572 // Skip null connections.
3573 if( bus_entry->Connection( &sheet )
3574 && bus_wire->Type() == SCH_LINE_T
3575 && bus_wire->Connection( &sheet ) )
3576 {
3577 conflict = true; // Assume a conflict; we'll reset if we find it's OK
3578
3579 bus_name = bus_wire->Connection( &sheet )->Name();
3580
3581 std::set<wxString> test_names;
3582 test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
3583
3584 wxString baseName = sheet.PathHumanReadable();
3585
3586 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3587 test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
3588
3589 for( const auto& member : bus_wire->Connection( &sheet )->Members() )
3590 {
3591 if( member->Type() == CONNECTION_TYPE::BUS )
3592 {
3593 for( const auto& sub_member : member->Members() )
3594 {
3595 if( test_names.count( sub_member->FullLocalName() ) )
3596 conflict = false;
3597 }
3598 }
3599 else if( test_names.count( member->FullLocalName() ) )
3600 {
3601 conflict = false;
3602 }
3603 }
3604 }
3605 }
3606
3607 // Don't report warnings if this bus member has been overridden by a higher priority power pin
3608 // or global label
3609 if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
3611 {
3612 conflict = false;
3613 }
3614
3615 if( conflict )
3616 {
3617 wxString netName = aSubgraph->m_driver_connection->Name();
3618 wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
3619 " member of that bus" ),
3620 UnescapeString( netName ),
3621 UnescapeString( bus_name ) );
3622 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
3623 ercItem->SetSheetSpecificPath( sheet );
3624 ercItem->SetItems( bus_entry, bus_wire );
3625 ercItem->SetErrorMessage( msg );
3626
3627 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), bus_entry->GetPosition() );
3628 screen->Append( marker );
3629
3630 return false;
3631 }
3632
3633 return true;
3634}
3635
3636
3638{
3639 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3640 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3641 SCH_SCREEN* screen = sheet.LastScreen();
3642 bool ok = true;
3643 SCH_PIN* pin = nullptr;
3644
3645 std::set<SCH_PIN*> unique_pins;
3646 std::set<SCH_LABEL_BASE*> unique_labels;
3647
3648 wxString netName = GetResolvedSubgraphName( aSubgraph );
3649
3650 auto process_subgraph = [&]( const CONNECTION_SUBGRAPH* aProcessGraph )
3651 {
3652 // Any subgraph that contains a no-connect should not
3653 // more than one pin (which would indicate it is connected
3654 for( SCH_ITEM* item : aProcessGraph->m_items )
3655 {
3656 switch( item->Type() )
3657 {
3658 case SCH_PIN_T:
3659 {
3660 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3661
3662 // Only link NC to pin on the current subgraph being checked
3663 if( aProcessGraph == aSubgraph )
3664 pin = test_pin;
3665
3666 if( std::none_of( unique_pins.begin(), unique_pins.end(),
3667 [test_pin]( SCH_PIN* aPin )
3668 {
3669 return test_pin->IsStacked( aPin );
3670 }
3671 ))
3672 {
3673 unique_pins.insert( test_pin );
3674 }
3675
3676 break;
3677 }
3678
3679 case SCH_LABEL_T:
3680 case SCH_GLOBAL_LABEL_T:
3681 case SCH_HIER_LABEL_T:
3682 unique_labels.insert( static_cast<SCH_LABEL_BASE*>( item ) );
3684 default:
3685 break;
3686 }
3687 }
3688 };
3689
3690 auto it = m_net_name_to_subgraphs_map.find( netName );
3691
3692 if( it != m_net_name_to_subgraphs_map.end() )
3693 {
3694 for( const CONNECTION_SUBGRAPH* subgraph : it->second )
3695 {
3696 process_subgraph( subgraph );
3697 }
3698 }
3699 else
3700 {
3701 process_subgraph( aSubgraph );
3702 }
3703
3704 if( aSubgraph->m_no_connect != nullptr )
3705 {
3706 // Special case: If the subgraph being checked consists of only a hier port/pin and
3707 // a no-connect, we don't issue a "no-connect connected" warning just because
3708 // connections exist on the sheet on the other side of the link.
3709 VECTOR2I noConnectPos = aSubgraph->m_no_connect->GetPosition();
3710
3711 for( SCH_SHEET_PIN* hierPin : aSubgraph->m_hier_pins )
3712 {
3713 if( hierPin->GetPosition() == noConnectPos )
3714 return true;
3715 }
3716
3717 for( SCH_HIERLABEL* hierLabel : aSubgraph->m_hier_ports )
3718 {
3719 if( hierLabel->GetPosition() == noConnectPos )
3720 return true;
3721 }
3722
3723 for( SCH_ITEM* item : screen->Items().Overlapping( SCH_SYMBOL_T, noConnectPos ) )
3724 {
3725 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3726
3727 const SCH_PIN* test_pin = symbol->GetPin( noConnectPos );
3728
3729 if( test_pin && test_pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
3730 return true;
3731 }
3732
3733 if( unique_pins.size() > 1 && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
3734 {
3735 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
3736 ercItem->SetSheetSpecificPath( sheet );
3737 ercItem->SetItemsSheetPaths( sheet );
3738
3739 VECTOR2I pos;
3740
3741 if( pin )
3742 {
3743 ercItem->SetItems( pin, aSubgraph->m_no_connect );
3744 pos = pin->GetPosition();
3745 }
3746 else
3747 {
3748 ercItem->SetItems( aSubgraph->m_no_connect );
3749 pos = aSubgraph->m_no_connect->GetPosition();
3750 }
3751
3752 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pos );
3753 screen->Append( marker );
3754
3755 ok = false;
3756 }
3757
3758 if( unique_pins.empty() && unique_labels.empty() &&
3760 {
3761 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
3762 ercItem->SetItems( aSubgraph->m_no_connect );
3763 ercItem->SetSheetSpecificPath( sheet );
3764 ercItem->SetItemsSheetPaths( sheet );
3765
3766 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), aSubgraph->m_no_connect->GetPosition() );
3767 screen->Append( marker );
3768
3769 ok = false;
3770 }
3771 }
3772 else
3773 {
3774 bool has_other_connections = false;
3775 std::vector<SCH_PIN*> pins;
3776
3777 // Any subgraph that lacks a no-connect and contains a pin should also
3778 // contain at least one other potential driver
3779
3780 for( SCH_ITEM* item : aSubgraph->m_items )
3781 {
3782 switch( item->Type() )
3783 {
3784 case SCH_PIN_T:
3785 {
3786 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3787
3788 // Stacked pins do not count as other connections but non-stacked pins do
3789 if( !has_other_connections && !pins.empty()
3790 && !test_pin->GetParentSymbol()->IsPower() )
3791 {
3792 for( SCH_PIN* other_pin : pins )
3793 {
3794 if( !test_pin->IsStacked( other_pin ) )
3795 {
3796 has_other_connections = true;
3797 break;
3798 }
3799 }
3800 }
3801
3802 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
3803
3804 break;
3805 }
3806
3807 default:
3808 if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
3809 has_other_connections = true;
3810
3811 break;
3812 }
3813 }
3814
3815 // For many checks, we can just use the first pin
3816 pin = pins.empty() ? nullptr : pins[0];
3817
3818 // But if there is a power pin, it might be connected elsewhere
3819 for( SCH_PIN* test_pin : pins )
3820 {
3821 // Prefer the pin is part of a real component rather than some stray power symbol
3822 // Or else we may fail walking connected components to a power symbol pin since we
3823 // reject starting at a power symbol
3824 if( test_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
3825 && !test_pin->IsPower() )
3826 {
3827 pin = test_pin;
3828 break;
3829 }
3830 }
3831
3832 // Check if power input pins connect to anything else via net name,
3833 // but not for power symbols (with visible or legacy invisible pins).
3834 // We want to throw unconnected errors for power symbols even if they are connected to other
3835 // net items by name, because usually failing to connect them graphically is a mistake
3836 if( pin && !has_other_connections
3837 && !pin->IsPower()
3838 && !pin->GetLibPin()->GetParentSymbol()->IsPower() )
3839 {
3840 wxString name = pin->Connection( &sheet )->Name();
3841 wxString local_name = pin->Connection( &sheet )->Name( true );
3842
3843 if( m_global_label_cache.count( name )
3844 || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
3845 {
3846 has_other_connections = true;
3847 }
3848 }
3849
3850 // Only one pin, and it's not a no-connect pin
3851 if( pin && !has_other_connections
3852 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
3853 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
3854 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3855 {
3856 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3857 ercItem->SetSheetSpecificPath( sheet );
3858 ercItem->SetItemsSheetPaths( sheet );
3859 ercItem->SetItems( pin );
3860
3861 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
3862 screen->Append( marker );
3863
3864 ok = false;
3865 }
3866
3867 // If there are multiple pins in this SG, they might be indirectly connected (by netname)
3868 // rather than directly connected (by wires). We want to flag dangling pins even if they
3869 // join nets with another pin, as it's often a mistake
3870 if( pins.size() > 1 )
3871 {
3872 for( SCH_PIN* testPin : pins )
3873 {
3874 // We only apply this test to power symbols, because other symbols have
3875 // pins that are meant to be dangling, but the power symbols have pins
3876 // that are *not* meant to be dangling.
3877 if( testPin->GetLibPin()->GetParentSymbol()->IsPower()
3878 && testPin->ConnectedItems( sheet ).empty()
3879 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3880 {
3881 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3882 ercItem->SetSheetSpecificPath( sheet );
3883 ercItem->SetItemsSheetPaths( sheet );
3884 ercItem->SetItems( testPin );
3885
3886 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), testPin->GetPosition() );
3887 screen->Append( marker );
3888
3889 ok = false;
3890 }
3891 }
3892 }
3893 }
3894
3895 return ok;
3896}
3897
3898
3900{
3901 int err_count = 0;
3902 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3903
3904 for( SCH_ITEM* item : aSubgraph->m_items )
3905 {
3906 if( item->GetLayer() != LAYER_WIRE )
3907 continue;
3908
3909 if( item->Type() == SCH_LINE_T )
3910 {
3911 SCH_LINE* line = static_cast<SCH_LINE*>( item );
3912
3913 if( line->IsGraphicLine() )
3914 continue;
3915
3916 auto report_error = [&]( VECTOR2I& location )
3917 {
3918 std::shared_ptr<ERC_ITEM> ercItem =
3920
3921 ercItem->SetItems( line );
3922 ercItem->SetSheetSpecificPath( sheet );
3923 ercItem->SetErrorMessage( _( "Unconnected wire endpoint" ) );
3924
3925 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), location );
3926 sheet.LastScreen()->Append( marker );
3927
3928 err_count++;
3929 };
3930
3931 if( line->IsStartDangling() )
3932 report_error( line->GetConnectionPoints()[0] );
3933
3934 if( line->IsEndDangling() )
3935 report_error( line->GetConnectionPoints()[1] );
3936 }
3937 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
3938 {
3939 SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3940
3941 auto report_error = [&]( VECTOR2I& location )
3942 {
3943 std::shared_ptr<ERC_ITEM> ercItem =
3945
3946 ercItem->SetItems( entry );
3947 ercItem->SetSheetSpecificPath( sheet );
3948 ercItem->SetErrorMessage( _( "Unconnected wire to bus entry" ) );
3949
3950 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), location );
3951 sheet.LastScreen()->Append( marker );
3952
3953 err_count++;
3954 };
3955
3956 if( entry->IsStartDangling() )
3957 report_error( entry->GetConnectionPoints()[0] );
3958
3959 if( entry->IsEndDangling() )
3960 report_error( entry->GetConnectionPoints()[1] );
3961 }
3962
3963 }
3964
3965 return err_count > 0;
3966}
3967
3968
3970{
3971 if( aSubgraph->m_driver )
3972 return true;
3973
3974 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3975 std::vector<SCH_ITEM*> wires;
3976
3977 // We've gotten this far, so we know we have no valid driver. All we need to do is check
3978 // for a wire that we can place the error on.
3979 for( SCH_ITEM* item : aSubgraph->m_items )
3980 {
3981 if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
3982 wires.emplace_back( item );
3983 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
3984 wires.emplace_back( item );
3985 }
3986
3987 if( !wires.empty() )
3988 {
3989 SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
3990
3991 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
3992 ercItem->SetSheetSpecificPath( sheet );
3993 ercItem->SetItems( wires[0],
3994 wires.size() > 1 ? wires[1] : nullptr,
3995 wires.size() > 2 ? wires[2] : nullptr,
3996 wires.size() > 3 ? wires[3] : nullptr );
3997
3998 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), wires[0]->GetPosition() );
3999 screen->Append( marker );
4000
4001 return false;
4002 }
4003
4004 return true;
4005}
4006
4007
4009{
4010 // Label connection rules:
4011 // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
4012 // Local labels are flagged if they don't connect to any pins and don't have a no-connect
4013 // Global labels are flagged if they appear only once, don't connect to any local labels,
4014 // and don't have a no-connect marker
4015
4016 if( !aSubgraph->m_driver_connection )
4017 return true;
4018
4019 // Buses are excluded from this test: many users create buses with only a single instance
4020 // and it's not really a problem as long as the nets in the bus pass ERC
4021 if( aSubgraph->m_driver_connection->IsBus() )
4022 return true;
4023
4024 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
4025 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4026 bool ok = true;
4027 size_t pinCount = 0;
4028 bool has_nc = !!aSubgraph->m_no_connect;
4029
4030 std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
4031
4032
4033 auto hasPins =
4034 []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> size_t
4035 {
4036 return std::count_if( aLocSubgraph->m_items.begin(), aLocSubgraph->m_items.end(),
4037 []( const SCH_ITEM* item )
4038 {
4039 return item->Type() == SCH_PIN_T;
4040 } );
4041 };
4042
4043 auto reportError =
4044 [&]( SCH_TEXT* aText, int errCode )
4045 {
4046 if( settings.IsTestEnabled( errCode ) )
4047 {
4048 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
4049 ercItem->SetSheetSpecificPath( sheet );
4050 ercItem->SetItems( aText );
4051
4052 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), aText->GetPosition() );
4053 aSubgraph->m_sheet.LastScreen()->Append( marker );
4054 }
4055 };
4056
4057 pinCount = hasPins( aSubgraph );
4058
4059 for( SCH_ITEM* item : aSubgraph->m_items )
4060 {
4061 switch( item->Type() )
4062 {
4063 case SCH_LABEL_T:
4064 case SCH_GLOBAL_LABEL_T:
4065 case SCH_HIER_LABEL_T:
4066 {
4067 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
4068
4069 label_map[item->Type()].push_back( text );
4070
4071 // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
4072 // we want to error if an individual label in the subgraph is floating, even if it's
4073 // connected to other valid things by way of another label on the same sheet.
4074 if( text->IsDangling() )
4075 {
4076 reportError( text, ERCE_LABEL_NOT_CONNECTED );
4077 return false;
4078 }
4079
4080 break;
4081 }
4082
4083 default:
4084 break;
4085 }
4086 }
4087
4088 if( label_map.empty() )
4089 return true;
4090
4091 // No-connects on net neighbors will be noticed before, but to notice them on bus parents we
4092 // need to walk the graph
4093 for( auto& [ connection, subgraphs ] : aSubgraph->m_bus_parents )
4094 {
4095 for( CONNECTION_SUBGRAPH* busParent : subgraphs )
4096 {
4097 if( busParent->m_no_connect )
4098 {
4099 has_nc = true;
4100 break;
4101 }
4102
4103 CONNECTION_SUBGRAPH* hp = busParent->m_hier_parent;
4104
4105 while( hp )
4106 {
4107 if( hp->m_no_connect )
4108 {
4109 has_nc = true;
4110 break;
4111 }
4112
4113 hp = hp->m_hier_parent;
4114 }
4115 }
4116 }
4117
4118 wxString netName = GetResolvedSubgraphName( aSubgraph );
4119
4120 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" ) );
4121
4122 // Labels that have multiple pins connected are not dangling (may be used for naming segments)
4123 // so leave them without errors here
4124 if( pinCount > 1 )
4125 return true;
4126
4127 for( auto& [type, label_vec] : label_map )
4128 {
4129 for( SCH_TEXT* text : label_vec )
4130 {
4131 size_t allPins = pinCount;
4132
4133 auto it = m_net_name_to_subgraphs_map.find( netName );
4134
4135 if( it != m_net_name_to_subgraphs_map.end() )
4136 {
4137 for( const CONNECTION_SUBGRAPH* neighbor : it->second )
4138 {
4139 if( neighbor == aSubgraph )
4140 continue;
4141
4142 if( neighbor->m_no_connect )
4143 has_nc = true;
4144
4145 allPins += hasPins( neighbor );
4146 }
4147 }
4148
4149 if( allPins == 1 && !has_nc )
4150 {
4151 reportError( text, ERCE_LABEL_SINGLE_PIN );
4152 ok = false;
4153 }
4154
4155 if( allPins == 0 )
4156 {
4157 reportError( text, ERCE_LABEL_NOT_CONNECTED );
4158 ok = false;
4159 }
4160 }
4161 }
4162
4163 return ok;
4164}
4165
4166
4168{
4169 int errors = 0;
4170
4171 std::map<wxString, std::tuple<int, const SCH_ITEM*, SCH_SHEET_PATH>> labelData;
4172
4173 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4174 {
4175 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
4176 {
4177 SCH_TEXT* labelText = static_cast<SCH_TEXT*>( item );
4178 wxString resolvedLabelText =
4179 EscapeString( labelText->GetShownText( &sheet, false ), CTX_NETNAME );
4180
4181 if( labelData.find( resolvedLabelText ) == labelData.end() )
4182 {
4183 labelData[resolvedLabelText] = { 1, item, sheet };
4184 }
4185 else
4186 {
4187 std::get<0>( labelData[resolvedLabelText] ) += 1;
4188 std::get<1>( labelData[resolvedLabelText] ) = nullptr;
4189 std::get<2>( labelData[resolvedLabelText] ) = sheet;
4190 }
4191 }
4192 }
4193
4194 for( const auto& label : labelData )
4195 {
4196 if( std::get<0>( label.second ) == 1 )
4197 {
4198 const SCH_SHEET_PATH& sheet = std::get<2>( label.second );
4199 const SCH_ITEM* item = std::get<1>( label.second );
4200
4201 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SINGLE_GLOBAL_LABEL );
4202 ercItem->SetItems( std::get<1>( label.second ) );
4203 ercItem->SetSheetSpecificPath( sheet );
4204 ercItem->SetItemsSheetPaths( sheet );
4205
4206 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), item->GetPosition() );
4207 sheet.LastScreen()->Append( marker );
4208
4209 errors++;
4210 }
4211 }
4212
4213 return errors;
4214}
4215
4216
4218{
4219 int error_count = 0;
4220
4221 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4222 {
4223 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_DIRECTIVE_LABEL_T ) )
4224 {
4225 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
4226
4227 if( label->IsDangling() )
4228 {
4229 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
4230 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
4231 ercItem->SetSheetSpecificPath( sheet );
4232 ercItem->SetItems( text );
4233
4234 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), text->GetPosition() );
4235 sheet.LastScreen()->Append( marker );
4236 error_count++;
4237 }
4238 }
4239 }
4240
4241 return error_count;
4242}
4243
4244
4246{
4247 wxString msg;
4248 int errors = 0;
4249
4250 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4251
4252 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4253 {
4254 // Hierarchical labels in the root sheet cannot be connected to anything.
4255 if( sheet.Last()->IsRootSheet() )
4256 {
4257 for( const SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
4258 {
4259 const SCH_HIERLABEL* label = static_cast<const SCH_HIERLABEL*>( item );
4260
4261 wxCHECK2( label, continue );
4262
4263 msg.Printf(
4264 _( "Hierarchical label '%s' in root sheet cannot be connected to non-existent parent sheet" ),
4265 label->GetShownText( &sheet, true ) );
4266 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4267 ercItem->SetItems( item );
4268 ercItem->SetErrorMessage( msg );
4269
4270 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), item->GetPosition() );
4271 sheet.LastScreen()->Append( marker );
4272
4273 errors++;
4274 }
4275 }
4276
4277 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
4278 {
4279 SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
4280 SCH_SHEET_PATH parentSheetPath = sheet;
4281
4282 parentSheetPath.push_back( parentSheet );
4283
4284 std::map<wxString, SCH_SHEET_PIN*> pins;
4285 std::map<wxString, SCH_HIERLABEL*> labels;
4286
4287 for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
4288 {
4289 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4290 pins[ pin->GetShownText( &parentSheetPath, false ) ] = pin;
4291
4292 if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
4293 {
4294 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4295 ercItem->SetItems( pin );
4296 ercItem->SetSheetSpecificPath( sheet );
4297 ercItem->SetItemsSheetPaths( sheet );
4298
4299 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
4300 sheet.LastScreen()->Append( marker );
4301
4302 errors++;
4303 }
4304 }
4305
4306 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4307 {
4308 std::set<wxString> matchedPins;
4309
4310 for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
4311 {
4312 if( subItem->Type() == SCH_HIER_LABEL_T )
4313 {
4314 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
4315 wxString labelText = label->GetShownText( &parentSheetPath, false );
4316
4317 if( !pins.count( labelText ) )
4318 labels[ labelText ] = label;
4319 else
4320 matchedPins.insert( labelText );
4321 }
4322 }
4323
4324 for( const wxString& matched : matchedPins )
4325 pins.erase( matched );
4326
4327 for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
4328 {
4329 msg.Printf( _( "Sheet pin %s has no matching hierarchical label inside the sheet" ),
4330 UnescapeString( unmatched.first ) );
4331
4332 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4333 ercItem->SetItems( unmatched.second );
4334 ercItem->SetErrorMessage( msg );
4335 ercItem->SetSheetSpecificPath( sheet );
4336 ercItem->SetItemsSheetPaths( sheet );
4337
4338 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ),
4339 unmatched.second->GetPosition() );
4340 sheet.LastScreen()->Append( marker );
4341
4342 errors++;
4343 }
4344
4345 for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
4346 {
4347 msg.Printf( _( "Hierarchical label %s has no matching sheet pin in the parent sheet" ),
4348 UnescapeString( unmatched.first ) );
4349
4350 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4351 ercItem->SetItems( unmatched.second );
4352 ercItem->SetErrorMessage( msg );
4353 ercItem->SetSheetSpecificPath( parentSheetPath );
4354 ercItem->SetItemsSheetPaths( parentSheetPath );
4355
4356 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ),
4357 unmatched.second->GetPosition() );
4358 parentSheet->GetScreen()->Append( marker );
4359
4360 errors++;
4361 }
4362 }
4363 }
4364 }
4365
4366 return errors;
4367}
const char * name
Definition: DXF_plotter.cpp:62
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:114
This represents a sentry transaction which is used for time-performance metrics You start a transacti...
Definition: app_monitor.h:64
void StartSpan(const std::string &aOperation, const std::string &aDescription)
Calculate the connectivity of a schematic and generates netlists.
int RunERC()
Run electrical rule checks on the connectivity graph.
bool ercCheckBusToBusConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for conflicting connections between two bus items.
void processSubGraphs()
Process all subgraphs to assign netcodes and merge subgraphs based on labels.
bool ercCheckLabels(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for proper connection of labels.
void RemoveItem(SCH_ITEM *aItem)
void collectAllDriverValues()
Map the driver values for each subgraph.
CONNECTION_SUBGRAPH * FindSubgraphByName(const wxString &aNetName, const SCH_SHEET_PATH &aPath)
Return the subgraph for a given net name on a given sheet.
int ercCheckDirectiveLabels()
Check directive labels should be connected to something.
void recacheSubgraphName(CONNECTION_SUBGRAPH *aSubgraph, const wxString &aOldName)
static SCH_CONNECTION * matchBusMember(SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
Search for a matching bus member inside a bus connection.
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
SCHEMATIC * m_schematic
The schematic this graph represents.
void updateGenericItemConnectivity(const SCH_SHEET_PATH &aSheet, SCH_ITEM *aItem, std::map< VECTOR2I, std::vector< SCH_ITEM * > > &aConnectionMap)
Update the connectivity of items that are not pins or symbols.
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
Cache to lookup subgraphs in m_driver_subgraphs by sheet path.
void updateSymbolConnectivity(const SCH_SHEET_PATH &aSheet, SCH_SYMBOL *aSymbol, std::map< VECTOR2I, std::vector< SCH_ITEM * > > &aConnectionMap)
Update the connectivity of a symbol and its pins.
CONNECTION_SUBGRAPH * FindFirstSubgraphByName(const wxString &aNetName)
Retrieve a subgraph for the given net name, if one exists.
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph, bool aForce)
Update all neighbors of a subgraph with this one's connectivity info.
void buildItemSubGraphs()
Generate individual item subgraphs on a per-sheet basis.
const std::vector< CONNECTION_SUBGRAPH * > & GetAllSubgraphs(const wxString &aNetName) const
bool ercCheckMultipleDrivers(const CONNECTION_SUBGRAPH *aSubgraph)
If the subgraph has multiple drivers of equal priority that are graphically connected,...
SCH_SHEET_LIST m_sheetList
All the sheets in the schematic (as long as we don't have partial updates).
void generateGlobalPowerPinSubGraphs()
Iterate through the global power pins to collect the global labels as drivers.
std::unordered_map< wxString, int > m_net_name_to_code_map
int ercCheckSingleGlobalLabel()
Check that a global label is instantiated more that once across the schematic hierarchy.
int ercCheckHierSheets()
Check that a hierarchical sheet has at least one matching label inside the sheet for each port on the...
bool ercCheckBusToNetConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for conflicting connections between net and bus labels.
std::shared_ptr< SCH_CONNECTION > getDefaultConnection(SCH_ITEM *aItem, CONNECTION_SUBGRAPH *aSubgraph)
Build a new default connection for the given item based on its properties.
std::vector< const CONNECTION_SUBGRAPH * > GetBusesNeedingMigration()
Determine which subgraphs have more than one conflicting bus label.
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
int assignNewNetCode(SCH_CONNECTION &aConnection)
Helper to assign a new net code to a connection.
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
int getOrCreateNetCode(const wxString &aNetName)
bool ercCheckDanglingWireEndpoints(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for dangling wire endpoints.
void assignNetCodesToBus(SCH_CONNECTION *aConnection)
Ensure all members of the bus connection have a valid net code assigned.
std::unordered_map< wxString, int > m_bus_name_to_code_map
std::unordered_map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
The owner of all CONNECTION_SUBGRAPH objects.
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_global_power_pins
bool ercCheckNoConnects(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for proper presence or absence of no-connect symbols.
size_t hasPins(const CONNECTION_SUBGRAPH *aLocSubgraph)
Get the number of pins in a given subgraph.
std::vector< SCH_ITEM * > m_items
All connectable items in the schematic.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
std::shared_ptr< BUS_ALIAS > GetBusAlias(const wxString &aName)
Return a bus alias pointer for the given name if it exists (from cache)
void removeSubgraphs(std::set< CONNECTION_SUBGRAPH * > &aSubgraphs)
Remove references to the given subgraphs from all structures in the connection graph.
std::unordered_map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map
std::set< std::pair< SCH_SHEET_PATH, SCH_ITEM * > > ExtractAffectedItems(const std::set< SCH_ITEM * > &aItems)
For a set of items, this will remove the connected items and their associated data including subgraph...
wxString GetResolvedSubgraphName(const CONNECTION_SUBGRAPH *aSubGraph) const
Return the fully-resolved netname for a given subgraph.
bool ercCheckBusToBusEntryConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for conflicting bus entry to bus connections.
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
Cache of a subset of m_subgraphs.
void ExchangeItem(SCH_ITEM *aOldItem, SCH_ITEM *aNewItem)
Replace all references to #aOldItem with #aNewItem in the graph.
NET_MAP m_net_code_to_subgraphs_map
bool ercCheckFloatingWires(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for floating wires.
void buildConnectionGraph(std::function< void(SCH_ITEM *)> *aChangedItemHandler, bool aUnconditional)
Generate the connection graph (after all item connectivity has been updated).
void Merge(CONNECTION_GRAPH &aGraph)
Combine the input graph contents into the current graph.
void updatePinConnectivity(const SCH_SHEET_PATH &aSheet, SCH_PIN *aPin, SCH_CONNECTION *aConnection)
Update the connectivity of a pin and its connections.
void resolveAllDrivers()
Find all subgraphs in the connection graph and calls ResolveDrivers() in parallel.
void updateItemConnectivity(const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
Update the graphical connectivity between items (i.e.
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem) const
void generateBusAliasMembers()
Iterate through labels to create placeholders for bus elements.
A subgraph is a set of items that are electrically connected on a single sheet.
wxString driverName(SCH_ITEM *aItem) const
PRIORITY GetDriverPriority()
bool m_strong_driver
True if the driver is "strong": a label or power object.
SCH_ITEM * m_no_connect
No-connect item in graph, if any.
std::set< CONNECTION_SUBGRAPH * > m_absorbed_subgraphs
Set of subgraphs that have been absorbed by this subgraph.
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Return the priority (higher is more important) of a candidate driver.
std::mutex m_driver_name_cache_mutex
A cache of escaped netnames from schematic items.
SCH_SHEET_PATH m_sheet
On which logical sheet is the subgraph contained.
void UpdateItemConnections()
Update all items to match the driver connection.
std::set< SCH_SHEET_PIN * > m_hier_pins
Cache for lookup of any hierarchical (sheet) pins on this subgraph (for referring down).
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...
CONNECTION_GRAPH * m_graph
std::vector< SCH_ITEM * > GetAllBusLabels() const
Return all the all bus labels attached to this subgraph (if any).
std::unordered_map< SCH_ITEM *, wxString > m_driver_name_cache
const wxString & GetNameForDriver(SCH_ITEM *aItem) const
Return the candidate net name for a driver.
wxString GetNetName() const
Return the fully-qualified net name for this subgraph (if one exists)
std::vector< SCH_ITEM * > GetVectorBusLabels() const
Return all the vector-based bus labels attached to this subgraph (if any).
const SCH_SHEET_PATH & GetSheet() const
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
bool ResolveDrivers(bool aCheckMultipleDrivers=false)
Determine which potential driver should drive the subgraph.
std::set< SCH_ITEM * > m_drivers
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
std::unordered_set< CONNECTION_SUBGRAPH * > m_hier_children
If not null, this indicates the subgraph(s) on a lower level sheet that are linked to this one.
void AddItem(SCH_ITEM *aItem)
Add a new item to the subgraph.
const std::vector< std::pair< wxString, SCH_ITEM * > > GetNetclassesForDriver(SCH_ITEM *aItem) const
Return the resolved netclasses for the item, and the source item providing the netclass.
void Absorb(CONNECTION_SUBGRAPH *aOther)
Combine another subgraph on the same sheet into this one.
std::set< SCH_ITEM * > m_items
Contents of the subgraph.
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.
SCH_ITEM * m_driver
Fully-resolved driver for the subgraph (might not exist in this subgraph).
bool m_is_bus_member
True if the subgraph is not actually part of a net.
void ExchangeItem(SCH_ITEM *aOldItem, SCH_ITEM *aNewItem)
Replaces all references to #aOldItem with #aNewItem in the subgraph.
CONNECTION_SUBGRAPH * m_hier_parent
If not null, this indicates the subgraph on a higher level sheet that is linked to this one.
void RemoveItem(SCH_ITEM *aItem)
bool m_local_driver
True if the driver is a local (i.e. non-global) type.
std::set< SCH_HIERLABEL * > m_hier_ports
Cache for lookup of any hierarchical ports on this subgraph (for referring up).
void getAllConnectedItems(std::set< std::pair< SCH_SHEET_PATH, SCH_ITEM * > > &aItems, std::set< CONNECTION_SUBGRAPH * > &aSubgraphs)
Find all items in the subgraph as well as child subgraphs recursively.
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:272
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition: eda_item.cpp:144
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:110
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition: sch_rtree.h:246
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:241
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:302
Container for ERC settings.
Definition: erc_settings.h:132
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:148
Definition: kiid.h:49
A small class to help profiling.
Definition: profile.h:49
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition: profile.h:105
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
std::shared_ptr< NET_SETTINGS > m_NetSettings
Net settings for this project (owned here)
Definition: project_file.h:189
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:204
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition: schematic.h:103
SCH_SHEET & Root() const
Definition: schematic.h:140
SCH_SHEET_PATH & CurrentSheet() const
Definition: schematic.h:171
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:363
Class for a bus to bus entry.
SCH_ITEM * m_connected_bus_items[2]
Pointer to the bus items (usually bus wires) connected to this bus-bus entry (either or both may be n...
bool IsStartDangling() const
Definition: sch_bus_entry.h:43
VECTOR2I GetPosition() const override
bool IsEndDangling() const
Definition: sch_bus_entry.h:44
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Class for a wire to bus entry.
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 ...
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString FullLocalName() const
void ConfigureFromLabel(const wxString &aLabel)
Configures the connection given a label.
bool IsNet() const
void SetSubgraphCode(int aCode)
void SetBusCode(int aCode)
void SetName(const wxString &aName)
SCH_SHEET_PATH Sheet() const
CONNECTION_TYPE Type() const
int SubgraphCode() const
void SetNetCode(int aCode)
SCH_ITEM * m_driver
The SCH_ITEM that drives this connection's net.
bool IsDriver() const
Checks if the SCH_ITEM this connection is attached to can drive connections Drivers can be labels,...
void SetType(CONNECTION_TYPE aType)
wxString LocalName() const
wxString Name(bool aIgnoreSheet=false) const
bool IsSubsetOf(SCH_CONNECTION *aOther) const
Returns true if this connection is contained within aOther (but not the same as aOther)
void SetDriver(SCH_ITEM *aItem)
bool IsBus() const
void Clone(const SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
void SetGraph(CONNECTION_GRAPH *aGraph)
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
long VectorIndex() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:168
void ClearConnectedItems(const SCH_SHEET_PATH &aPath)
Clear all connections to this item.
Definition: sch_item.cpp:402
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition: sch_item.h:602
const SYMBOL * GetParentSymbol() const
Definition: sch_item.cpp:252
virtual const wxString & GetCachedDriverName() const
Definition: sch_item.cpp:472
const std::unordered_set< SCH_RULE_AREA * > & GetRuleAreaCache() const
Get the cache of rule areas enclosing this item.
Definition: sch_item.h:650
SCH_CONNECTION * InitializeConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Create a new connection object associated with this object.
Definition: sch_item.cpp:433
void AddConnectionTo(const SCH_SHEET_PATH &aPath, SCH_ITEM *aItem)
Add a connection link between this item and another.
Definition: sch_item.cpp:417
int GetUnit() const
Definition: sch_item.h:239
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:313
void SetConnectionGraph(CONNECTION_GRAPH *aGraph)
Update the connection graph for all connections in this item.
Definition: sch_item.cpp:369
virtual bool HasCachedDriverName() const
Definition: sch_item.h:587
SCH_CONNECTION * GetOrInitConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Definition: sch_item.cpp:457
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:352
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition: sch_item.h:513
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const override
Definition: sch_label.cpp:891
bool IsDangling() const override
Definition: sch_label.h:329
LABEL_FLAG_SHAPE GetShape() const
Definition: sch_label.h:176
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:42
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Definition: sch_line.cpp:685
bool IsStartDangling() const
Definition: sch_line.h:294
bool IsEndDangling() const
Definition: sch_line.h:295
bool IsGraphicLine() const
Return if the line is a graphic (non electrical line)
Definition: sch_line.cpp:943
bool IsGlobalPower() const
Return whether this pin forms a global power connection: i.e., is part of a power symbol and of type ...
Definition: sch_pin.cpp:323
bool IsLocalPower() const
Local power pin is the same except that it is sheet-local and it does not support the legacy hidden p...
Definition: sch_pin.cpp:330
SCH_PIN * GetLibPin() const
Definition: sch_pin.h:88
bool IsStacked(const SCH_PIN *aPin) const
Definition: sch_pin.cpp:432
wxString GetDefaultNetName(const SCH_SHEET_PATH &aPath, bool aForceNoConnect=false)
Definition: sch_pin.cpp:1200
bool IsPower() const
Check if the pin is either a global or local power pin.
Definition: sch_pin.cpp:337
ELECTRICAL_PINTYPE GetType() const
Definition: sch_pin.cpp:269
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:758
SCH_SCREEN * GetNext()
SCH_SCREEN * GetFirst()
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Definition: sch_screen.cpp:160
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition: sch_screen.h:117
SCH_LINE * GetBus(const VECTOR2I &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T) const
Definition: sch_screen.h:442
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
const SCH_SHEET * GetSheet(unsigned aIndex) const
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false) const
Return the sheet path in a human readable form made from the sheet names.
SCH_SCREEN * LastScreen()
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:66
SCH_SHEET * GetParent() const
Get the parent sheet object of this sheet pin.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:47
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:321
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:116
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:187
Schematic symbol object.
Definition: sch_symbol.h:75
bool IsInNetlist() const
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:686
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition: sch_symbol.h:183
VECTOR2I GetPosition() const override
Definition: sch_text.h:141
virtual wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
Definition: sch_text.cpp:318
virtual bool IsGlobalPower() const =0
virtual bool IsLocalPower() const =0
virtual bool IsPower() const =0
bool GetExcludedFromBoard() const override
Definition: symbol.h:187
The common library.
#define _(s)
@ NO_RECURSE
Definition: eda_item.h:52
#define CONNECTIVITY_CANDIDATE
flag indicating that the structure is connected for connectivity
@ ERCE_DRIVER_CONFLICT
Conflicting drivers (labels, etc) on a subgraph.
Definition: erc_settings.h:65
@ ERCE_UNCONNECTED_WIRE_ENDPOINT
A label is connected to more than one wire.
Definition: erc_settings.h:89
@ ERCE_LABEL_NOT_CONNECTED
Label not connected to any pins.
Definition: erc_settings.h:49
@ ERCE_BUS_TO_BUS_CONFLICT
A connection between bus objects doesn't share at least one net.
Definition: erc_settings.h:67
@ ERCE_LABEL_SINGLE_PIN
A label is connected only to a single pin.
Definition: erc_settings.h:72
@ ERCE_BUS_ENTRY_CONFLICT
A wire connected to a bus doesn't match the bus.
Definition: erc_settings.h:66
@ ERCE_BUS_TO_NET_CONFLICT
A bus wire is graphically connected to a net port/pin (or vice versa).
Definition: erc_settings.h:69
@ ERCE_NOCONNECT_NOT_CONNECTED
A no connect symbol is not connected to anything.
Definition: erc_settings.h:48
@ ERCE_PIN_NOT_CONNECTED
Pin not connected and not no connect symbol.
Definition: erc_settings.h:41
@ ERCE_NOCONNECT_CONNECTED
A no connect symbol is connected to more than 1 pin.
Definition: erc_settings.h:47
@ ERCE_HIERACHICAL_LABEL
Mismatch between hierarchical labels and pins sheets.
Definition: erc_settings.h:46
@ ERCE_WIRE_DANGLING
Some wires are not connected to anything else.
Definition: erc_settings.h:76
@ ERCE_SINGLE_GLOBAL_LABEL
A label only exists once in the schematic.
Definition: erc_settings.h:53
static const wxChar DanglingProfileMask[]
Flag to enable connectivity profiling.
static const wxChar ConnTrace[]
Flag to enable connectivity tracing.
@ LAYER_WIRE
Definition: layer_ids.h:442
@ LAYER_BUS
Definition: layer_ids.h:443
@ LAYER_JUNCTION
Definition: layer_ids.h:444
@ LAYER_BUS_JUNCTION
Definition: layer_ids.h:487
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition: kicad_algo.h:161
CONNECTION_TYPE
std::vector< SCH_ITEM * > SCH_ITEM_VEC
Definition: sch_item.h:157
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
Definition: string_utils.h:53
VECTOR2I location
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:30
static thread_pool * tp
Definition: thread_pool.cpp:28
BS::thread_pool thread_pool
Definition: thread_pool.h:31
#define kv
@ SCH_LINE_T
Definition: typeinfo.h:164
@ SCH_NO_CONNECT_T
Definition: typeinfo.h:161
@ SCH_SYMBOL_T
Definition: typeinfo.h:173
@ SCH_FIELD_T
Definition: typeinfo.h:151
@ SCH_DIRECTIVE_LABEL_T
Definition: typeinfo.h:172
@ SCH_LABEL_T
Definition: typeinfo.h:168
@ SCH_SHEET_T
Definition: typeinfo.h:176
@ SCH_HIER_LABEL_T
Definition: typeinfo.h:170
@ SCH_BUS_BUS_ENTRY_T
Definition: typeinfo.h:163
@ SCH_SHEET_PIN_T
Definition: typeinfo.h:175
@ SCH_TEXT_T
Definition: typeinfo.h:152
@ SCH_BUS_WIRE_ENTRY_T
Definition: typeinfo.h:162
@ SCH_GLOBAL_LABEL_T
Definition: typeinfo.h:169
@ SCH_JUNCTION_T
Definition: typeinfo.h:160
@ SCH_PIN_T
Definition: typeinfo.h:154
Functions to provide common constants and other functions to assist in making a consistent UI.