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 ) );
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 ) );
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 auto dirtySubgraphs =
2377 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2378 {
2379 if( aChangedItemHandler )
2380 {
2381 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2382 {
2383 for( SCH_ITEM* item : subgraph->m_items )
2384 (*aChangedItemHandler)( item );
2385 }
2386 }
2387 };
2388
2389 auto checkNetclassDrivers =
2390 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2391 {
2392 const CONNECTION_SUBGRAPH* driverSubgraph = nullptr;
2393 wxString netclass;
2394
2395 wxCHECK_RET( !subgraphs.empty(), wxS( "Invalid empty subgraph" ) );
2396
2397 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2398 {
2399 for( SCH_ITEM* item : subgraph->m_items )
2400 {
2401 const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProviders =
2402 subgraph->GetNetclassesForDriver( item, false );
2403
2404 if( netclassesWithProviders.size() > 0 )
2405 {
2406 netclass = netclassesWithProviders[0].first;
2407 break;
2408 }
2409 }
2410
2411 if( !netclass.IsEmpty() )
2412 {
2413 driverSubgraph = subgraph;
2414 break;
2415 }
2416 }
2417
2418 if( !driverSubgraph )
2419 driverSubgraph = subgraphs.front();
2420
2421 const wxString netname = driverSubgraph->GetNetName();
2422
2423 if( driverSubgraph->m_driver_connection->IsBus() )
2424 {
2425 for( const auto& member : driverSubgraph->m_driver_connection->Members() )
2426 {
2427 if( netclass.IsEmpty() )
2428 netSettings->m_NetClassLabelAssignments.erase( member->Name() );
2429 else
2430 netSettings->m_NetClassLabelAssignments[member->Name()] = netclass;
2431
2432 auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
2433
2434 if( ii != m_net_name_to_subgraphs_map.end() )
2435 dirtySubgraphs( ii->second );
2436 }
2437 }
2438
2439 if( netclass.IsEmpty() )
2440 netSettings->m_NetClassLabelAssignments.erase( netname );
2441 else
2442 netSettings->m_NetClassLabelAssignments[netname] = netclass;
2443
2444 if( oldAssignments.count( netname ) )
2445 {
2446 if( oldAssignments[netname] != netclass )
2447 {
2448 affectedNetclassNetAssignments.insert( netname );
2449 dirtySubgraphs( subgraphs );
2450 }
2451 }
2452 else if( !netclass.IsEmpty() )
2453 {
2454 affectedNetclassNetAssignments.insert( netname );
2455 dirtySubgraphs( subgraphs );
2456 }
2457 };
2458
2459 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
2460 checkNetclassDrivers( subgraphs );
2461
2462 if( !aUnconditional )
2463 {
2464 for( auto& [ netname, netclass ] : oldAssignments )
2465 {
2466 if( netSettings->m_NetClassLabelAssignments.count( netname )
2467 || affectedNetclassNetAssignments.count( netname ) )
2468 {
2469 continue;
2470 }
2471
2472 netSettings->m_NetClassLabelAssignments[ netname ] = netclass;
2473 }
2474 }
2475}
2476
2477
2478int CONNECTION_GRAPH::getOrCreateNetCode( const wxString& aNetName )
2479{
2480 int code;
2481
2482 auto it = m_net_name_to_code_map.find( aNetName );
2483
2484 if( it == m_net_name_to_code_map.end() )
2485 {
2486 code = m_last_net_code++;
2487 m_net_name_to_code_map[ aNetName ] = code;
2488 }
2489 else
2490 {
2491 code = it->second;
2492 }
2493
2494 return code;
2495}
2496
2497
2499{
2500 int code = getOrCreateNetCode( aConnection.Name() );
2501
2502 aConnection.SetNetCode( code );
2503
2504 return code;
2505}
2506
2507
2509{
2510 std::vector<std::shared_ptr<SCH_CONNECTION>> connections_to_check( aConnection->Members() );
2511
2512 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2513 {
2514 const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
2515
2516 if( member->IsBus() )
2517 {
2518 connections_to_check.insert( connections_to_check.end(),
2519 member->Members().begin(),
2520 member->Members().end() );
2521 continue;
2522 }
2523
2524 assignNewNetCode( *member );
2525 }
2526}
2527
2528
2530{
2531 SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
2532 std::vector<CONNECTION_SUBGRAPH*> search_list;
2533 std::unordered_set<CONNECTION_SUBGRAPH*> visited;
2534 std::unordered_set<SCH_CONNECTION*> stale_bus_members;
2535
2536 auto visit =[&]( CONNECTION_SUBGRAPH* aParent )
2537 {
2538 for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
2539 {
2540 SCH_SHEET_PATH path = aParent->m_sheet;
2541 path.push_back( pin->GetParent() );
2542
2543 auto it = m_sheet_to_subgraphs_map.find( path );
2544
2545 if( it == m_sheet_to_subgraphs_map.end() )
2546 continue;
2547
2548 for( CONNECTION_SUBGRAPH* candidate : it->second )
2549 {
2550 if( !candidate->m_strong_driver
2551 || candidate->m_hier_ports.empty()
2552 || visited.contains( candidate ) )
2553 {
2554 continue;
2555 }
2556
2557 for( SCH_HIERLABEL* label : candidate->m_hier_ports )
2558 {
2559 if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
2560 {
2561 wxLogTrace( ConnTrace, wxS( "%lu: found child %lu (%s)" ), aParent->m_code,
2562 candidate->m_code, candidate->m_driver_connection->Name() );
2563
2564 candidate->m_hier_parent = aParent;
2565 aParent->m_hier_children.insert( candidate );
2566
2567 wxASSERT( candidate->m_graph == aParent->m_graph );
2568
2569 search_list.push_back( candidate );
2570 break;
2571 }
2572 }
2573 }
2574 }
2575
2576 for( SCH_HIERLABEL* label : aParent->m_hier_ports )
2577 {
2578 SCH_SHEET_PATH path = aParent->m_sheet;
2579 path.pop_back();
2580
2581 auto it = m_sheet_to_subgraphs_map.find( path );
2582
2583 if( it == m_sheet_to_subgraphs_map.end() )
2584 continue;
2585
2586 for( CONNECTION_SUBGRAPH* candidate : it->second )
2587 {
2588 if( candidate->m_hier_pins.empty()
2589 || visited.contains( candidate )
2590 || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
2591 {
2592 continue;
2593 }
2594
2595 const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
2596
2597 for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
2598 {
2599 // If the last sheet UUIDs won't match, no need to check the full path
2600 if( pin->GetParent()->m_Uuid != last_parent_uuid )
2601 continue;
2602
2603 SCH_SHEET_PATH pin_path = path;
2604 pin_path.push_back( pin->GetParent() );
2605
2606 if( pin_path != aParent->m_sheet )
2607 continue;
2608
2609 if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
2610 {
2611 wxLogTrace( ConnTrace, wxS( "%lu: found additional parent %lu (%s)" ),
2612 aParent->m_code, candidate->m_code,
2613 candidate->m_driver_connection->Name() );
2614
2615 aParent->m_hier_children.insert( candidate );
2616 search_list.push_back( candidate );
2617 break;
2618 }
2619 }
2620 }
2621 }
2622 };
2623
2624 auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph )
2625 {
2626 for( const auto& kv : aParentGraph->m_bus_neighbors )
2627 {
2628 for( CONNECTION_SUBGRAPH* neighbor : kv.second )
2629 {
2630 // May have been absorbed but won't have been deleted
2631 while( neighbor->m_absorbed )
2632 neighbor = neighbor->m_absorbed_by;
2633
2634 SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
2635
2636 // Now member may be out of date, since we just cloned the
2637 // connection from higher up in the hierarchy. We need to
2638 // figure out what the actual new connection is.
2639 SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
2640
2641 if( !member )
2642 {
2643 // Try harder: we might match on a secondary driver
2644 for( CONNECTION_SUBGRAPH* sg : kv.second )
2645 {
2646 if( sg->m_multiple_drivers )
2647 {
2648 SCH_SHEET_PATH sheet = sg->m_sheet;
2649
2650 for( SCH_ITEM* driver : sg->m_drivers )
2651 {
2652 auto c = getDefaultConnection( driver, sg );
2653 member = matchBusMember( parent, c.get() );
2654
2655 if( member )
2656 break;
2657 }
2658 }
2659
2660 if( member )
2661 break;
2662 }
2663 }
2664
2665 // This is bad, probably an ERC error
2666 if( !member )
2667 {
2668 wxLogTrace( ConnTrace, wxS( "Could not match bus member %s in %s" ),
2669 kv.first->Name(), parent->Name() );
2670 continue;
2671 }
2672
2673 auto neighbor_conn = neighbor->m_driver_connection;
2674 auto neighbor_name = neighbor_conn->Name();
2675
2676 // Matching name: no update needed
2677 if( neighbor_name == member->Name() )
2678 continue;
2679
2680 // Was this neighbor already updated from a different sheet? Don't rename it again
2681 if( neighbor_conn->Sheet() != neighbor->m_sheet )
2682 continue;
2683
2684 // Safety check against infinite recursion
2685 wxASSERT( neighbor_conn->IsNet() );
2686
2687 wxLogTrace( ConnTrace, wxS( "%lu (%s) connected to bus member %s (local %s)" ),
2688 neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
2689
2690 // Take whichever name is higher priority
2693 {
2694 member->Clone( *neighbor_conn );
2695 stale_bus_members.insert( member );
2696 }
2697 else
2698 {
2699 neighbor_conn->Clone( *member );
2700
2701 recacheSubgraphName( neighbor, neighbor_name );
2702
2703 // Recurse onto this neighbor in case it needs to re-propagate
2704 neighbor->m_dirty = true;
2705 propagateToNeighbors( neighbor, aForce );
2706 }
2707 }
2708 }
2709 };
2710
2711 // If we are a bus, we must propagate to local neighbors and then the hierarchy
2712 if( conn->IsBus() )
2713 propagate_bus_neighbors( aSubgraph );
2714
2715 // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
2716 // If we only have one or the other, process (we can either go bottom-up or top-down depending
2717 // on which subgraph comes up first)
2718 if( !aForce && !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
2719 {
2720 wxLogTrace( ConnTrace, wxS( "%lu (%s) has both hier ports and pins; deferring processing" ),
2721 aSubgraph->m_code, conn->Name() );
2722 return;
2723 }
2724 else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
2725 {
2726 wxLogTrace( ConnTrace,
2727 wxS( "%lu (%s) has no hier pins or ports on sheet %s; marking clean" ),
2728 aSubgraph->m_code, conn->Name(), aSubgraph->m_sheet.PathHumanReadable() );
2729 aSubgraph->m_dirty = false;
2730 return;
2731 }
2732
2733 visited.insert( aSubgraph );
2734
2735 wxLogTrace( ConnTrace, wxS( "Propagating %lu (%s) to subsheets" ),
2736 aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
2737
2738 visit( aSubgraph );
2739
2740 for( unsigned i = 0; i < search_list.size(); i++ )
2741 {
2742 auto child = search_list[i];
2743
2744 if( visited.insert( child ).second )
2745 visit( child );
2746
2747 child->m_dirty = false;
2748 }
2749
2750 // Now, find the best driver for this chain of subgraphs
2751 CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
2754 bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2755 wxString bestName = aSubgraph->m_driver_connection->Name();
2756
2757 // Check if a subsheet has a higher-priority connection to the same net
2759 {
2760 for( CONNECTION_SUBGRAPH* subgraph : visited )
2761 {
2762 if( subgraph == aSubgraph )
2763 continue;
2764
2766 CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
2767
2768 bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2769 wxString candidateName = subgraph->m_driver_connection->Name();
2770 bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
2771 bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
2772
2773 // Pick a better driving subgraph if it:
2774 // a) has a power pin or global driver
2775 // b) is a strong driver and we're a weak driver
2776 // c) is a higher priority strong driver
2777 // d) matches our priority, is a strong driver, and has a shorter path
2778 // e) matches our strength and is at least as short, and is alphabetically lower
2779
2780 if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
2781 ( !bestIsStrong && candidateStrong ) ||
2782 ( priority > highest && candidateStrong ) ||
2783 ( priority == highest && candidateStrong && shorterPath ) ||
2784 ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
2785 ( candidateName < bestName ) ) )
2786 {
2787 bestDriver = subgraph;
2788 highest = priority;
2789 bestIsStrong = candidateStrong;
2790 bestName = candidateName;
2791 }
2792 }
2793 }
2794
2795 if( bestDriver != aSubgraph )
2796 {
2797 wxLogTrace( ConnTrace, wxS( "%lu (%s) overridden by new driver %lu (%s)" ),
2798 aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
2799 bestDriver->m_driver_connection->Name() );
2800 }
2801
2802 conn = bestDriver->m_driver_connection;
2803
2804 for( CONNECTION_SUBGRAPH* subgraph : visited )
2805 {
2806 wxString old_name = subgraph->m_driver_connection->Name();
2807
2808 subgraph->m_driver_connection->Clone( *conn );
2809
2810 if( old_name != conn->Name() )
2811 recacheSubgraphName( subgraph, old_name );
2812
2813 if( conn->IsBus() )
2814 propagate_bus_neighbors( subgraph );
2815 }
2816
2817 // Somewhere along the way, a bus member may have been upgraded to a global or power label.
2818 // Because this can happen anywhere, we need a second pass to update all instances of that bus
2819 // member to have the correct connection info
2820 if( conn->IsBus() && !stale_bus_members.empty() )
2821 {
2822 std::unordered_set<SCH_CONNECTION*> cached_members = stale_bus_members;
2823
2824 for( SCH_CONNECTION* stale_member : cached_members )
2825 {
2826 for( CONNECTION_SUBGRAPH* subgraph : visited )
2827 {
2828 SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
2829 stale_member );
2830
2831 if( !member )
2832 {
2833 wxLogTrace( ConnTrace, wxS( "WARNING: failed to match stale member %s in %s." ),
2834 stale_member->Name(), subgraph->m_driver_connection->Name() );
2835 continue;
2836 }
2837
2838 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ), subgraph->m_code,
2839 subgraph->m_driver_connection->Name(), member->LocalName(),
2840 stale_member->Name() );
2841
2842 member->Clone( *stale_member );
2843
2844 propagate_bus_neighbors( subgraph );
2845 }
2846 }
2847 }
2848
2849 aSubgraph->m_dirty = false;
2850}
2851
2852
2853std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
2854 CONNECTION_SUBGRAPH* aSubgraph )
2855{
2856 std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
2857
2858 switch( aItem->Type() )
2859 {
2860 case SCH_PIN_T:
2861 {
2862 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
2863
2864 if( pin->IsGlobalPower() )
2865 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2866
2867 break;
2868 }
2869
2870 case SCH_GLOBAL_LABEL_T:
2871 case SCH_HIER_LABEL_T:
2872 case SCH_LABEL_T:
2873 {
2874 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2875 break;
2876 }
2877
2878 default:
2879 break;
2880 }
2881
2882 if( c )
2883 {
2884 c->SetGraph( this );
2885 c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
2886 }
2887
2888 return c;
2889}
2890
2891
2893 SCH_CONNECTION* aSearch )
2894{
2895 wxASSERT( aBusConnection->IsBus() );
2896
2897 SCH_CONNECTION* match = nullptr;
2898
2899 if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
2900 {
2901 // Vector bus: compare against index, because we allow the name
2902 // to be different
2903
2904 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
2905 {
2906 if( bus_member->VectorIndex() == aSearch->VectorIndex() )
2907 {
2908 match = bus_member.get();
2909 break;
2910 }
2911 }
2912 }
2913 else
2914 {
2915 // Group bus
2916 for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
2917 {
2918 // Vector inside group: compare names, because for bus groups
2919 // we expect the naming to be consistent across all usages
2920 // TODO(JE) explain this in the docs
2921 if( c->Type() == CONNECTION_TYPE::BUS )
2922 {
2923 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
2924 {
2925 if( bus_member->LocalName() == aSearch->LocalName() )
2926 {
2927 match = bus_member.get();
2928 break;
2929 }
2930 }
2931 }
2932 else if( c->LocalName() == aSearch->LocalName() )
2933 {
2934 match = c.get();
2935 break;
2936 }
2937 }
2938 }
2939
2940 return match;
2941}
2942
2943
2945 const wxString& aOldName )
2946{
2947 auto it = m_net_name_to_subgraphs_map.find( aOldName );
2948
2949 if( it != m_net_name_to_subgraphs_map.end() )
2950 {
2951 std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
2952 alg::delete_matching( vec, aSubgraph );
2953 }
2954
2955 wxLogTrace( ConnTrace, wxS( "recacheSubgraphName: %s => %s" ), aOldName,
2956 aSubgraph->m_driver_connection->Name() );
2957
2958 m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
2959}
2960
2961
2962std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
2963{
2964 auto it = m_bus_alias_cache.find( aName );
2965
2966 return it != m_bus_alias_cache.end() ? it->second : nullptr;
2967}
2968
2969
2970std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
2971{
2972 std::vector<const CONNECTION_SUBGRAPH*> ret;
2973
2974 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
2975 {
2976 // Graph is supposed to be up-to-date before calling this
2977 wxASSERT( !subgraph->m_dirty );
2978
2979 if( !subgraph->m_driver )
2980 continue;
2981
2982 SCH_SHEET_PATH* sheet = &subgraph->m_sheet;
2983 SCH_CONNECTION* connection = subgraph->m_driver->Connection( sheet );
2984
2985 if( !connection->IsBus() )
2986 continue;
2987
2988 auto labels = subgraph->GetVectorBusLabels();
2989
2990 if( labels.size() > 1 )
2991 {
2992 bool different = false;
2993 wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText( sheet, false );
2994
2995 for( unsigned i = 1; i < labels.size(); ++i )
2996 {
2997 if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText( sheet, false ) != first )
2998 {
2999 different = true;
3000 break;
3001 }
3002 }
3003
3004 if( !different )
3005 continue;
3006
3007 wxLogTrace( ConnTrace, wxS( "SG %ld (%s) has multiple bus labels" ), subgraph->m_code,
3008 connection->Name() );
3009
3010 ret.push_back( subgraph );
3011 }
3012 }
3013
3014 return ret;
3015}
3016
3017
3019{
3020 wxString retval = aSubGraph->GetNetName();
3021 bool found = false;
3022
3023 // This is a hacky way to find the true subgraph net name (why do we not store it?)
3024 // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
3025
3026 for( auto it = m_net_name_to_subgraphs_map.begin();
3027 it != m_net_name_to_subgraphs_map.end() && !found; ++it )
3028 {
3029 for( CONNECTION_SUBGRAPH* graph : it->second )
3030 {
3031 if( graph == aSubGraph )
3032 {
3033 retval = it->first;
3034 found = true;
3035 break;
3036 }
3037 }
3038 }
3039
3040 return retval;
3041}
3042
3043
3045 const SCH_SHEET_PATH& aPath )
3046{
3047 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3048
3049 if( it == m_net_name_to_subgraphs_map.end() )
3050 return nullptr;
3051
3052 for( CONNECTION_SUBGRAPH* sg : it->second )
3053 {
3054 // Cache is supposed to be valid by now
3055 wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
3056
3057 if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
3058 return sg;
3059 }
3060
3061 return nullptr;
3062}
3063
3064
3066{
3067 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3068
3069 if( it == m_net_name_to_subgraphs_map.end() )
3070 return nullptr;
3071
3072 wxASSERT( !it->second.empty() );
3073
3074 return it->second[0];
3075}
3076
3077
3079{
3080 auto it = m_item_to_subgraph_map.find( aItem );
3081 CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
3082
3083 while( ret && ret->m_absorbed )
3084 ret = ret->m_absorbed_by;
3085
3086 return ret;
3087}
3088
3089
3090const std::vector<CONNECTION_SUBGRAPH*>
3091CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
3092{
3093 std::vector<CONNECTION_SUBGRAPH*> subgraphs;
3094
3095 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3096
3097 if( it == m_net_name_to_subgraphs_map.end() )
3098 return subgraphs;
3099
3100 return it->second;
3101}
3102
3103
3105{
3106 int error_count = 0;
3107
3108 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::RunERC" ) );
3109
3110 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3111
3112 // We don't want to run many ERC checks more than once on a given screen even though it may
3113 // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
3114 std::set<SCH_ITEM*> seenDriverInstances;
3115
3116 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3117 {
3118 // There shouldn't be any null sub-graph pointers.
3119 wxCHECK2( subgraph, continue );
3120
3121 // Graph is supposed to be up-to-date before calling RunERC()
3122 wxASSERT( !subgraph->m_dirty );
3123
3124 if( subgraph->m_absorbed )
3125 continue;
3126
3127 if( seenDriverInstances.count( subgraph->m_driver ) )
3128 continue;
3129
3130 if( subgraph->m_driver )
3131 seenDriverInstances.insert( subgraph->m_driver );
3132
3143 if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
3144 {
3145 if( !ercCheckMultipleDrivers( subgraph ) )
3146 error_count++;
3147 }
3148
3149 subgraph->ResolveDrivers( false );
3150
3151 if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
3152 {
3153 if( !ercCheckBusToNetConflicts( subgraph ) )
3154 error_count++;
3155 }
3156
3157 if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
3158 {
3159 if( !ercCheckBusToBusEntryConflicts( subgraph ) )
3160 error_count++;
3161 }
3162
3163 if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
3164 {
3165 if( !ercCheckBusToBusConflicts( subgraph ) )
3166 error_count++;
3167 }
3168
3169 if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
3170 {
3171 if( !ercCheckFloatingWires( subgraph ) )
3172 error_count++;
3173 }
3174
3177 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3178 {
3179 if( !ercCheckNoConnects( subgraph ) )
3180 error_count++;
3181 }
3182
3184 || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
3185 {
3186 if( !ercCheckLabels( subgraph ) )
3187 error_count++;
3188 }
3189 }
3190
3191 // Hierarchical sheet checking is done at the schematic level
3193 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3194 {
3195 error_count += ercCheckHierSheets();
3196 }
3197
3198 if( settings.IsTestEnabled( ERCE_NETCLASS_CONFLICT ) )
3199 {
3200 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
3201 {
3202 if( !ercCheckNetclassConflicts( subgraphs ) )
3203 error_count++;
3204 }
3205 }
3206
3207 if( settings.IsTestEnabled( ERCE_SINGLE_GLOBAL_LABEL ) )
3208 {
3209 error_count += ercCheckSingleGlobalLabel();
3210 }
3211
3212 return error_count;
3213}
3214
3215
3217{
3218 wxCHECK( aSubgraph, false );
3219
3220 if( aSubgraph->m_multiple_drivers )
3221 {
3222 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3223 {
3224 if( driver == aSubgraph->m_driver )
3225 continue;
3226
3227 if( driver->Type() == SCH_GLOBAL_LABEL_T
3228 || driver->Type() == SCH_HIER_LABEL_T
3229 || driver->Type() == SCH_LABEL_T
3230 || ( driver->Type() == SCH_PIN_T
3231 && static_cast<SCH_PIN*>( driver )->IsGlobalPower() ) )
3232 {
3233 const wxString& primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
3234 const wxString& secondaryName = aSubgraph->GetNameForDriver( driver );
3235
3236 if( primaryName == secondaryName )
3237 continue;
3238
3239 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
3240 "items; %s will be used in the netlist" ),
3241 primaryName, secondaryName, primaryName );
3242
3243 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
3244 ercItem->SetItems( aSubgraph->m_driver, driver );
3245 ercItem->SetSheetSpecificPath( aSubgraph->GetSheet() );
3246 ercItem->SetItemsSheetPaths( aSubgraph->GetSheet(), aSubgraph->m_sheet );
3247 ercItem->SetErrorMessage( msg );
3248
3249 SCH_MARKER* marker = new SCH_MARKER( ercItem, driver->GetPosition() );
3250 aSubgraph->m_sheet.LastScreen()->Append( marker );
3251
3252 return false;
3253 }
3254 }
3255 }
3256
3257 return true;
3258}
3259
3260
3261bool CONNECTION_GRAPH::ercCheckNetclassConflicts( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
3262{
3263 wxString firstNetclass;
3264 SCH_ITEM* firstNetclassDriver = nullptr;
3265 const SCH_SHEET_PATH* firstNetclassDriverSheet = nullptr;
3266 bool conflictFound = false;
3267
3268 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
3269 {
3270 for( SCH_ITEM* item : subgraph->m_items )
3271 {
3272 const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProvider =
3273 subgraph->GetNetclassesForDriver( item, true );
3274
3275 if( netclassesWithProvider.size() == 0 )
3276 continue;
3277
3278 auto checkNetclass = [&]( const std::pair<wxString, SCH_ITEM*>& netclass )
3279 {
3280 if( netclass.first != firstNetclass )
3281 {
3282 if( !firstNetclassDriver )
3283 {
3284 firstNetclass = netclass.first;
3285 firstNetclassDriver = netclass.second;
3286 firstNetclassDriverSheet = &subgraph->GetSheet();
3287 }
3288 else
3289 {
3290 conflictFound = true;
3291
3292 std::shared_ptr<ERC_ITEM> ercItem =
3294 ercItem->SetItems( firstNetclassDriver, netclass.second );
3295 ercItem->SetSheetSpecificPath( subgraph->GetSheet() );
3296 ercItem->SetItemsSheetPaths( *firstNetclassDriverSheet,
3297 subgraph->GetSheet() );
3298
3299 SCH_MARKER* marker =
3300 new SCH_MARKER( ercItem, netclass.second->GetPosition() );
3301 subgraph->m_sheet.LastScreen()->Append( marker );
3302 }
3303 }
3304 };
3305
3306 for( const std::pair<wxString, SCH_ITEM*>& netclass : netclassesWithProvider )
3307 checkNetclass( netclass );
3308 }
3309 }
3310
3311 return conflictFound;
3312}
3313
3314
3316{
3317 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3318 SCH_SCREEN* screen = sheet.LastScreen();
3319
3320 SCH_ITEM* net_item = nullptr;
3321 SCH_ITEM* bus_item = nullptr;
3322 SCH_CONNECTION conn( this );
3323
3324 for( SCH_ITEM* item : aSubgraph->m_items )
3325 {
3326 switch( item->Type() )
3327 {
3328 case SCH_LINE_T:
3329 {
3330 if( item->GetLayer() == LAYER_BUS )
3331 bus_item = ( !bus_item ) ? item : bus_item;
3332 else
3333 net_item = ( !net_item ) ? item : net_item;
3334
3335 break;
3336 }
3337
3338 case SCH_LABEL_T:
3339 case SCH_GLOBAL_LABEL_T:
3340 case SCH_SHEET_PIN_T:
3341 case SCH_HIER_LABEL_T:
3342 {
3343 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3344 conn.ConfigureFromLabel( EscapeString( text->GetShownText( &sheet, false ),
3345 CTX_NETNAME ) );
3346
3347 if( conn.IsBus() )
3348 bus_item = ( !bus_item ) ? item : bus_item;
3349 else
3350 net_item = ( !net_item ) ? item : net_item;
3351
3352 break;
3353 }
3354
3355 default:
3356 break;
3357 }
3358 }
3359
3360 if( net_item && bus_item )
3361 {
3362 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
3363 ercItem->SetSheetSpecificPath( sheet );
3364 ercItem->SetItems( net_item, bus_item );
3365
3366 SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
3367 screen->Append( marker );
3368
3369 return false;
3370 }
3371
3372 return true;
3373}
3374
3375
3377{
3378 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3379 SCH_SCREEN* screen = sheet.LastScreen();
3380
3381 SCH_ITEM* label = nullptr;
3382 SCH_ITEM* port = nullptr;
3383
3384 for( SCH_ITEM* item : aSubgraph->m_items )
3385 {
3386 switch( item->Type() )
3387 {
3388 case SCH_TEXT_T:
3389 case SCH_GLOBAL_LABEL_T:
3390 {
3391 if( !label && item->Connection( &sheet )->IsBus() )
3392 label = item;
3393 break;
3394 }
3395
3396 case SCH_SHEET_PIN_T:
3397 case SCH_HIER_LABEL_T:
3398 {
3399 if( !port && item->Connection( &sheet )->IsBus() )
3400 port = item;
3401 break;
3402 }
3403
3404 default:
3405 break;
3406 }
3407 }
3408
3409 if( label && port )
3410 {
3411 bool match = false;
3412
3413 for( const auto& member : label->Connection( &sheet )->Members() )
3414 {
3415 for( const auto& test : port->Connection( &sheet )->Members() )
3416 {
3417 if( test != member && member->Name() == test->Name() )
3418 {
3419 match = true;
3420 break;
3421 }
3422 }
3423
3424 if( match )
3425 break;
3426 }
3427
3428 if( !match )
3429 {
3430 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
3431 ercItem->SetSheetSpecificPath( sheet );
3432 ercItem->SetItems( label, port );
3433
3434 SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
3435 screen->Append( marker );
3436
3437 return false;
3438 }
3439 }
3440
3441 return true;
3442}
3443
3444
3446{
3447 bool conflict = false;
3448 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3449 SCH_SCREEN* screen = sheet.LastScreen();
3450
3451 SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
3452 SCH_ITEM* bus_wire = nullptr;
3453 wxString bus_name;
3454
3455 if( !aSubgraph->m_driver_connection )
3456 {
3457 // Incomplete bus entry. Let the unconnected tests handle it.
3458 return true;
3459 }
3460
3461 for( SCH_ITEM* item : aSubgraph->m_items )
3462 {
3463 switch( item->Type() )
3464 {
3466 {
3467 if( !bus_entry )
3468 bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3469 break;
3470 }
3471
3472 default:
3473 break;
3474 }
3475 }
3476
3477 if( bus_entry && bus_entry->m_connected_bus_item )
3478 {
3479 bus_wire = bus_entry->m_connected_bus_item;
3480
3481 wxASSERT( bus_wire->Type() == SCH_LINE_T );
3482
3483 // In some cases, the connection list (SCH_CONNECTION*) can be null.
3484 // Skip null connections.
3485 if( bus_entry->Connection( &sheet )
3486 && bus_wire->Type() == SCH_LINE_T
3487 && bus_wire->Connection( &sheet ) )
3488 {
3489 conflict = true; // Assume a conflict; we'll reset if we find it's OK
3490
3491 bus_name = bus_wire->Connection( &sheet )->Name();
3492
3493 std::set<wxString> test_names;
3494 test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
3495
3496 wxString baseName = sheet.PathHumanReadable();
3497
3498 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3499 test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
3500
3501 for( const auto& member : bus_wire->Connection( &sheet )->Members() )
3502 {
3503 if( member->Type() == CONNECTION_TYPE::BUS )
3504 {
3505 for( const auto& sub_member : member->Members() )
3506 {
3507 if( test_names.count( sub_member->FullLocalName() ) )
3508 conflict = false;
3509 }
3510 }
3511 else if( test_names.count( member->FullLocalName() ) )
3512 {
3513 conflict = false;
3514 }
3515 }
3516 }
3517 }
3518
3519 // Don't report warnings if this bus member has been overridden by a higher priority power pin
3520 // or global label
3521 if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
3523 {
3524 conflict = false;
3525 }
3526
3527 if( conflict )
3528 {
3529 wxString netName = aSubgraph->m_driver_connection->Name();
3530 wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
3531 " member of that bus" ),
3532 UnescapeString( netName ),
3533 UnescapeString( bus_name ) );
3534 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
3535 ercItem->SetSheetSpecificPath( sheet );
3536 ercItem->SetItems( bus_entry, bus_wire );
3537 ercItem->SetErrorMessage( msg );
3538
3539 SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
3540 screen->Append( marker );
3541
3542 return false;
3543 }
3544
3545 return true;
3546}
3547
3548
3550{
3551 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3552 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3553 SCH_SCREEN* screen = sheet.LastScreen();
3554 bool ok = true;
3555 SCH_PIN* pin = nullptr;
3556
3557 std::set<SCH_PIN*> unique_pins;
3558 std::set<SCH_LABEL_BASE*> unique_labels;
3559
3560 wxString netName = GetResolvedSubgraphName( aSubgraph );
3561
3562 auto process_subgraph = [&]( const CONNECTION_SUBGRAPH* aProcessGraph )
3563 {
3564 // Any subgraph that contains a no-connect should not
3565 // more than one pin (which would indicate it is connected
3566 for( SCH_ITEM* item : aProcessGraph->m_items )
3567 {
3568 switch( item->Type() )
3569 {
3570 case SCH_PIN_T:
3571 {
3572 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3573
3574 // Only link NC to pin on the current subgraph being checked
3575 if( aProcessGraph == aSubgraph )
3576 pin = test_pin;
3577
3578 if( std::none_of( unique_pins.begin(), unique_pins.end(),
3579 [test_pin]( SCH_PIN* aPin )
3580 {
3581 return test_pin->IsStacked( aPin );
3582 }
3583 ))
3584 {
3585 unique_pins.insert( test_pin );
3586 }
3587
3588 break;
3589 }
3590
3591 case SCH_LABEL_T:
3592 case SCH_GLOBAL_LABEL_T:
3593 case SCH_HIER_LABEL_T:
3594 unique_labels.insert( static_cast<SCH_LABEL_BASE*>( item ) );
3596 default:
3597 break;
3598 }
3599 }
3600 };
3601
3602 auto it = m_net_name_to_subgraphs_map.find( netName );
3603
3604 if( it != m_net_name_to_subgraphs_map.end() )
3605 {
3606 for( const CONNECTION_SUBGRAPH* subgraph : it->second )
3607 {
3608 process_subgraph( subgraph );
3609 }
3610 }
3611 else
3612 {
3613 process_subgraph( aSubgraph );
3614 }
3615
3616 if( aSubgraph->m_no_connect != nullptr )
3617 {
3618 // Special case: If the subgraph being checked consists of only a hier port/pin and
3619 // a no-connect, we don't issue a "no-connect connected" warning just because
3620 // connections exist on the sheet on the other side of the link.
3621 VECTOR2I noConnectPos = aSubgraph->m_no_connect->GetPosition();
3622
3623 for( SCH_SHEET_PIN* hierPin : aSubgraph->m_hier_pins )
3624 {
3625 if( hierPin->GetPosition() == noConnectPos )
3626 return true;
3627 }
3628
3629 for( SCH_HIERLABEL* hierLabel : aSubgraph->m_hier_ports )
3630 {
3631 if( hierLabel->GetPosition() == noConnectPos )
3632 return true;
3633 }
3634
3635 if( unique_pins.size() > 1 && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
3636 {
3637 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
3638 ercItem->SetSheetSpecificPath( sheet );
3639 ercItem->SetItemsSheetPaths( sheet );
3640
3641 VECTOR2I pos;
3642
3643 if( pin )
3644 {
3645 ercItem->SetItems( pin, aSubgraph->m_no_connect );
3646 pos = pin->GetPosition();
3647 }
3648 else
3649 {
3650 ercItem->SetItems( aSubgraph->m_no_connect );
3651 pos = aSubgraph->m_no_connect->GetPosition();
3652 }
3653
3654 SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
3655 screen->Append( marker );
3656
3657 ok = false;
3658 }
3659
3660 if( unique_pins.empty() && unique_labels.empty() &&
3662 {
3663 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
3664 ercItem->SetItems( aSubgraph->m_no_connect );
3665 ercItem->SetSheetSpecificPath( sheet );
3666 ercItem->SetItemsSheetPaths( sheet );
3667
3668 SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
3669 screen->Append( marker );
3670
3671 ok = false;
3672 }
3673 }
3674 else
3675 {
3676 bool has_other_connections = false;
3677 std::vector<SCH_PIN*> pins;
3678
3679 // Any subgraph that lacks a no-connect and contains a pin should also
3680 // contain at least one other potential driver
3681
3682 for( SCH_ITEM* item : aSubgraph->m_items )
3683 {
3684 switch( item->Type() )
3685 {
3686 case SCH_PIN_T:
3687 {
3688 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3689
3690 // Stacked pins do not count as other connections but non-stacked pins do
3691 if( !has_other_connections && !pins.empty() && !test_pin->GetParentSymbol()->IsPower() )
3692 {
3693 for( SCH_PIN* other_pin : pins )
3694 {
3695 if( !test_pin->IsStacked( other_pin ) )
3696 {
3697 has_other_connections = true;
3698 break;
3699 }
3700 }
3701 }
3702
3703 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
3704
3705 break;
3706 }
3707
3708 default:
3709 if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
3710 has_other_connections = true;
3711
3712 break;
3713 }
3714 }
3715
3716 // For many checks, we can just use the first pin
3717 pin = pins.empty() ? nullptr : pins[0];
3718
3719 // But if there is a power pin, it might be connected elsewhere
3720 for( SCH_PIN* test_pin : pins )
3721 {
3722 // Prefer the pin is part of a real component rather than some stray power symbol
3723 // Or else we may fail walking connected components to a power symbol pin since we reject
3724 // starting at a power symbol
3725 if( test_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
3726 && !test_pin->IsGlobalPower() )
3727 {
3728 pin = test_pin;
3729 break;
3730 }
3731 }
3732
3733 // Check if power input pins connect to anything else via net name,
3734 // but not for power symbols (with visible or legacy invisible pins).
3735 // We want to throw unconnected errors for power symbols even if they are connected to other
3736 // net items by name, because usually failing to connect them graphically is a mistake
3737 if( pin && !has_other_connections
3738 && !pin->IsGlobalPower()
3739 && !pin->GetLibPin()->GetParentSymbol()->IsPower() )
3740 {
3741 wxString name = pin->Connection( &sheet )->Name();
3742 wxString local_name = pin->Connection( &sheet )->Name( true );
3743
3744 if( m_global_label_cache.count( name )
3745 || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
3746 {
3747 has_other_connections = true;
3748 }
3749 }
3750
3751 // Only one pin, and it's not a no-connect pin
3752 if( pin && !has_other_connections
3753 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
3754 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
3755 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3756 {
3757 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3758 ercItem->SetSheetSpecificPath( sheet );
3759 ercItem->SetItemsSheetPaths( sheet );
3760 ercItem->SetItems( pin );
3761
3762 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
3763 screen->Append( marker );
3764
3765 ok = false;
3766 }
3767
3768 // If there are multiple pins in this SG, they might be indirectly connected (by netname)
3769 // rather than directly connected (by wires). We want to flag dangling pins even if they
3770 // join nets with another pin, as it's often a mistake
3771 if( pins.size() > 1 )
3772 {
3773 for( SCH_PIN* testPin : pins )
3774 {
3775 // We only apply this test to power symbols, because other symbols have
3776 // pins that are meant to be dangling, but the power symbols have pins
3777 // that are *not* meant to be dangling.
3778 if( testPin->GetLibPin()->GetParentSymbol()->IsPower()
3779 && testPin->ConnectedItems( sheet ).empty()
3780 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3781 {
3782 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3783 ercItem->SetSheetSpecificPath( sheet );
3784 ercItem->SetItemsSheetPaths( sheet );
3785 ercItem->SetItems( testPin );
3786
3787 SCH_MARKER* marker = new SCH_MARKER( ercItem, testPin->GetPosition() );
3788 screen->Append( marker );
3789
3790 ok = false;
3791 }
3792 }
3793 }
3794 }
3795
3796 return ok;
3797}
3798
3799
3801{
3802 if( aSubgraph->m_driver )
3803 return true;
3804
3805 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3806 std::vector<SCH_ITEM*> wires;
3807
3808 // We've gotten this far, so we know we have no valid driver. All we need to do is check
3809 // for a wire that we can place the error on.
3810 for( SCH_ITEM* item : aSubgraph->m_items )
3811 {
3812 if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
3813 wires.emplace_back( item );
3814 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
3815 wires.emplace_back( item );
3816 }
3817
3818 if( !wires.empty() )
3819 {
3820 SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
3821
3822 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
3823 ercItem->SetSheetSpecificPath( sheet );
3824 ercItem->SetItems( wires[0],
3825 wires.size() > 1 ? wires[1] : nullptr,
3826 wires.size() > 2 ? wires[2] : nullptr,
3827 wires.size() > 3 ? wires[3] : nullptr );
3828
3829 SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
3830 screen->Append( marker );
3831
3832 return false;
3833 }
3834
3835 return true;
3836}
3837
3838
3840{
3841 // Label connection rules:
3842 // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
3843 // Local labels are flagged if they don't connect to any pins and don't have a no-connect
3844 // Global labels are flagged if they appear only once, don't connect to any local labels,
3845 // and don't have a no-connect marker
3846
3847 if( !aSubgraph->m_driver_connection )
3848 return true;
3849
3850 // Buses are excluded from this test: many users create buses with only a single instance
3851 // and it's not really a problem as long as the nets in the bus pass ERC
3852 if( aSubgraph->m_driver_connection->IsBus() )
3853 return true;
3854
3855 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3856 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3857 bool ok = true;
3858 size_t pinCount = 0;
3859 bool has_nc = !!aSubgraph->m_no_connect;
3860
3861 std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
3862
3863
3864 auto hasPins =
3865 []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> size_t
3866 {
3867 return std::count_if( aLocSubgraph->m_items.begin(), aLocSubgraph->m_items.end(),
3868 []( const SCH_ITEM* item )
3869 {
3870 return item->Type() == SCH_PIN_T;
3871 } );
3872 };
3873
3874 auto reportError =
3875 [&]( SCH_TEXT* aText, int errCode )
3876 {
3877 if( settings.IsTestEnabled( errCode ) )
3878 {
3879 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
3880 ercItem->SetSheetSpecificPath( sheet );
3881 ercItem->SetItems( aText );
3882
3883 SCH_MARKER* marker = new SCH_MARKER( ercItem, aText->GetPosition() );
3884 aSubgraph->m_sheet.LastScreen()->Append( marker );
3885 }
3886 };
3887
3888 pinCount = hasPins( aSubgraph );
3889
3890 for( SCH_ITEM* item : aSubgraph->m_items )
3891 {
3892 switch( item->Type() )
3893 {
3894 case SCH_LABEL_T:
3895 case SCH_GLOBAL_LABEL_T:
3896 case SCH_HIER_LABEL_T:
3897 {
3898 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3899
3900 label_map[item->Type()].push_back( text );
3901
3902 // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
3903 // we want to error if an individual label in the subgraph is floating, even if it's
3904 // connected to other valid things by way of another label on the same sheet.
3905 if( text->IsDangling() )
3906 {
3907 reportError( text, ERCE_LABEL_NOT_CONNECTED );
3908 return false;
3909 }
3910
3911 break;
3912 }
3913
3914 default:
3915 break;
3916 }
3917 }
3918
3919 if( label_map.empty() )
3920 return true;
3921
3922 // No-connects on net neighbors will be noticed before, but to notice them on bus parents we
3923 // need to walk the graph
3924 for( auto& [ connection, subgraphs ] : aSubgraph->m_bus_parents )
3925 {
3926 for( CONNECTION_SUBGRAPH* busParent : subgraphs )
3927 {
3928 if( busParent->m_no_connect )
3929 {
3930 has_nc = true;
3931 break;
3932 }
3933
3934 CONNECTION_SUBGRAPH* hp = busParent->m_hier_parent;
3935
3936 while( hp )
3937 {
3938 if( hp->m_no_connect )
3939 {
3940 has_nc = true;
3941 break;
3942 }
3943
3944 hp = hp->m_hier_parent;
3945 }
3946 }
3947 }
3948
3949 wxString netName = GetResolvedSubgraphName( aSubgraph );
3950
3951 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" ) );
3952
3953 // Labels that have multiple pins connected are not dangling (may be used for naming segments)
3954 // so leave them without errors here
3955 if( pinCount > 1 )
3956 return true;
3957
3958 for( auto& [type, label_vec] : label_map )
3959 {
3960 switch( type )
3961 {
3962 case SCH_GLOBAL_LABEL_T:
3963 if( !settings.IsTestEnabled( ERCE_GLOBLABEL ) )
3964 continue;
3965
3966 break;
3967 default:
3968 if( !settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
3969 continue;
3970
3971 break;
3972 }
3973
3974 for( SCH_TEXT* text : label_vec )
3975 {
3976 size_t allPins = pinCount;
3977
3978 auto it = m_net_name_to_subgraphs_map.find( netName );
3979
3980 if( it != m_net_name_to_subgraphs_map.end() )
3981 {
3982 for( const CONNECTION_SUBGRAPH* neighbor : it->second )
3983 {
3984 if( neighbor == aSubgraph )
3985 continue;
3986
3987 if( neighbor->m_no_connect )
3988 has_nc = true;
3989
3990 allPins += hasPins( neighbor );
3991 }
3992 }
3993
3994 if( allPins == 1 && !has_nc )
3995 {
3996 reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL
3998 ok = false;
3999 }
4000
4001 if( allPins == 0 )
4002 {
4003 reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL
4005 ok = false;
4006 }
4007 }
4008 }
4009
4010 return ok;
4011}
4012
4013
4015{
4016 int errors = 0;
4017
4018 std::map<wxString, std::tuple<int, const SCH_ITEM*, SCH_SHEET_PATH>> labelData;
4019
4020 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4021 {
4022 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
4023 {
4024 SCH_TEXT* labelText = static_cast<SCH_TEXT*>( item );
4025 wxString resolvedLabelText =
4026 EscapeString( labelText->GetShownText( &sheet, false ), CTX_NETNAME );
4027
4028 if( labelData.find( resolvedLabelText ) == labelData.end() )
4029 {
4030 labelData[resolvedLabelText] = { 1, item, sheet };
4031 }
4032 else
4033 {
4034 std::get<0>( labelData[resolvedLabelText] ) += 1;
4035 std::get<1>( labelData[resolvedLabelText] ) = nullptr;
4036 std::get<2>( labelData[resolvedLabelText] ) = sheet;
4037 }
4038 }
4039 }
4040
4041 for( const auto& label : labelData )
4042 {
4043 if( std::get<0>( label.second ) == 1 )
4044 {
4045 const SCH_SHEET_PATH& sheet = std::get<2>( label.second );
4046 const SCH_ITEM* item = std::get<1>( label.second );
4047
4048 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SINGLE_GLOBAL_LABEL );
4049 ercItem->SetItems( std::get<1>( label.second ) );
4050 ercItem->SetSheetSpecificPath( sheet );
4051 ercItem->SetItemsSheetPaths( sheet );
4052
4053 SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
4054 sheet.LastScreen()->Append( marker );
4055
4056 errors++;
4057 }
4058 }
4059
4060 return errors;
4061}
4062
4063
4065{
4066 int errors = 0;
4067
4068 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4069
4070 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4071 {
4072 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
4073 {
4074 SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
4075 SCH_SHEET_PATH parentSheetPath = sheet;
4076
4077 parentSheetPath.push_back( parentSheet );
4078
4079 std::map<wxString, SCH_SHEET_PIN*> pins;
4080 std::map<wxString, SCH_HIERLABEL*> labels;
4081
4082 for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
4083 {
4084 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4085 pins[ pin->GetShownText( &parentSheetPath, false ) ] = pin;
4086
4087 if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
4088 {
4089 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4090 ercItem->SetItems( pin );
4091 ercItem->SetSheetSpecificPath( sheet );
4092 ercItem->SetItemsSheetPaths( sheet );
4093
4094 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
4095 sheet.LastScreen()->Append( marker );
4096
4097 errors++;
4098 }
4099 }
4100
4101 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4102 {
4103 std::set<wxString> matchedPins;
4104
4105 for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
4106 {
4107 if( subItem->Type() == SCH_HIER_LABEL_T )
4108 {
4109 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
4110 wxString labelText = label->GetShownText( &parentSheetPath, false );
4111
4112 if( !pins.count( labelText ) )
4113 labels[ labelText ] = label;
4114 else
4115 matchedPins.insert( labelText );
4116 }
4117 }
4118
4119 for( const wxString& matched : matchedPins )
4120 pins.erase( matched );
4121
4122 for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
4123 {
4124 wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
4125 "label inside the sheet" ),
4126 UnescapeString( unmatched.first ) );
4127
4128 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4129 ercItem->SetItems( unmatched.second );
4130 ercItem->SetErrorMessage( msg );
4131 ercItem->SetSheetSpecificPath( sheet );
4132 ercItem->SetItemsSheetPaths( sheet );
4133
4134 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
4135 sheet.LastScreen()->Append( marker );
4136
4137 errors++;
4138 }
4139
4140 for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
4141 {
4142 wxString msg = wxString::Format( _( "Hierarchical label %s has no matching "
4143 "sheet pin in the parent sheet" ),
4144 UnescapeString( unmatched.first ) );
4145
4146 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4147 ercItem->SetItems( unmatched.second );
4148 ercItem->SetErrorMessage( msg );
4149 ercItem->SetSheetSpecificPath( parentSheetPath );
4150 ercItem->SetItemsSheetPaths( parentSheetPath );
4151
4152 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
4153 parentSheet->GetScreen()->Append( marker );
4154
4155 errors++;
4156 }
4157 }
4158 }
4159 }
4160
4161 return errors;
4162}
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:242
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:100
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider) const
Return a user-visible description string of this item.
Definition: eda_item.cpp:108
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:98
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:262
Container for ERC settings.
Definition: erc_settings.h:122
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:138
Definition: kiid.h:49
SCH_FIELD & GetReferenceField() const
Return reference to the reference designator field.
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:143
SCH_SHEET & Root() const
Definition: schematic.h:112
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:90
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:296
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:266
const SYMBOL * GetParentSymbol() const
Definition: sch_item.cpp:161
virtual const wxString & GetCachedDriverName() const
Definition: sch_item.cpp:336
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:281
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:233
virtual bool HasCachedDriverName() const
Definition: sch_item.h:538
SCH_CONNECTION * GetOrInitConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Definition: sch_item.cpp:321
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:216
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const override
Definition: sch_label.cpp:892
LABEL_FLAG_SHAPE GetShape() const
Definition: sch_label.h:172
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:703
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.
LIB_SYMBOL & GetLibSymbolRef()
Definition: sch_symbol.h:198
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:776
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:405
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:63
@ 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:65
@ ERCE_BUS_ENTRY_CONFLICT
A wire connected to a bus doesn't match the bus.
Definition: erc_settings.h:64
@ ERCE_NETCLASS_CONFLICT
Multiple labels assign different netclasses to same net.
Definition: erc_settings.h:69
@ ERCE_GLOBLABEL
A global label is unique.
Definition: erc_settings.h:71
@ ERCE_BUS_TO_NET_CONFLICT
A bus wire is graphically connected to a net port/pin (or vice versa).
Definition: erc_settings.h:67
@ 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:75
@ ERCE_SINGLE_GLOBAL_LABEL
A global label only exists once in the schematic.
Definition: erc_settings.h:52
static const wxChar DanglingProfileMask[]
Flag to enable connectivity profiling.
static const wxChar ConnTrace[]
Flag to enable connectivity tracing.
@ LAYER_WIRE
Definition: layer_ids.h:356
@ LAYER_BUS
Definition: layer_ids.h:357
@ LAYER_JUNCTION
Definition: layer_ids.h:358
@ LAYER_BUS_JUNCTION
Definition: layer_ids.h:399
#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.