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