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.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->GetText();
473
474 if( netclass != wxEmptyString )
475 foundNetclasses.push_back( { field->GetText(), 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 // If the symbol isn't dirty, look at the pins
759 // TODO: remove symbols from connectivity graph and only use pins
760 else if( item->Type() == SCH_SYMBOL_T )
761 {
762 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
763
764 for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
765 {
766 if( pin->IsConnectivityDirty() )
767 {
768 items.push_back( pin );
769 dirty_items.insert( pin );
770 }
771 }
772 }
773 else if( item->Type() == SCH_SHEET_T )
774 {
775 SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
776
777 for( SCH_SHEET_PIN* pin : sheetItem->GetPins() )
778 {
779 if( pin->IsConnectivityDirty() )
780 {
781 items.push_back( pin );
782 dirty_items.insert( pin );
783 }
784 }
785 }
786
787 // Ensure the hierarchy info stored in the SCH_SCREEN (such as symbol units) reflects
788 // the current SCH_SHEET_PATH
789 if( item->Type() == SCH_SYMBOL_T )
790 {
791 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
792 int new_unit = symbol->GetUnitSelection( &sheet );
793
794 // Store the initial unit value so we can restore it after calculations
795 if( symbol->GetUnit() != new_unit )
796 symbolsChanged.push_back( { symbol, symbol->GetUnit() } );
797
798 symbol->SetUnit( new_unit );
799 }
800 }
801
802 m_items.reserve( m_items.size() + items.size() );
803
804 updateItemConnectivity( sheet, items );
805
806 // UpdateDanglingState() also adds connected items for SCH_TEXT
807 sheet.LastScreen()->TestDanglingEnds( &sheet, aChangedItemHandler );
808
809 // Restore the m_unit member variables where we had to change them
810 for( const auto& [ symbol, originalUnit ] : symbolsChanged )
811 symbol->SetUnit( originalUnit );
812 }
813
814 // Restore the danlging states of items in the current SCH_SCREEN to match the current
815 // SCH_SHEET_PATH.
816 m_schematic->CurrentSheet().LastScreen()->TestDanglingEnds( &m_schematic->CurrentSheet(),
817 aChangedItemHandler );
818
819 for( SCH_ITEM* item : dirty_items )
820 item->SetConnectivityDirty( false );
821
822 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
823 update_items.Show();
824
825 PROF_TIMER build_graph( "buildConnectionGraph" );
826
827 buildConnectionGraph( aChangedItemHandler, aUnconditional );
828
829 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
830 build_graph.Show();
831
832 recalc_time.Stop();
833
834 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
835 recalc_time.Show();
836}
837
838
839std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> CONNECTION_GRAPH::ExtractAffectedItems(
840 const std::set<SCH_ITEM*> &aItems )
841{
842 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> retvals;
843 std::set<CONNECTION_SUBGRAPH*> subgraphs;
844
845 auto traverse_subgraph = [&retvals, &subgraphs]( CONNECTION_SUBGRAPH* aSubgraph )
846 {
847 // Find the primary subgraph on this sheet
848 while( aSubgraph->m_absorbed_by )
849 {
850 wxASSERT( aSubgraph->m_graph == aSubgraph->m_absorbed_by->m_graph );
851 aSubgraph = aSubgraph->m_absorbed_by;
852 }
853
854 // Find the top most connected subgraph on all sheets
855 while( aSubgraph->m_hier_parent )
856 {
857 wxASSERT( aSubgraph->m_graph == aSubgraph->m_hier_parent->m_graph );
858 aSubgraph = aSubgraph->m_hier_parent;
859 }
860
861 // Recurse through all subsheets to collect connected items
862 aSubgraph->getAllConnectedItems( retvals, subgraphs );
863 };
864
865 auto extract_element = [&]( SCH_ITEM* aItem )
866 {
867 CONNECTION_SUBGRAPH* item_sg = GetSubgraphForItem( aItem );
868
869 if( !item_sg )
870 {
871 wxLogTrace( ConnTrace, wxT( "Item %s not found in connection graph" ), aItem->GetTypeDesc() );
872 return;
873 }
874 if( !item_sg->ResolveDrivers( true ) )
875 {
876 wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld (%p) has no driver" ),
877 aItem->GetTypeDesc(), item_sg->m_code, item_sg );
878 }
879
880 std::vector<CONNECTION_SUBGRAPH*> sg_to_scan = GetAllSubgraphs( item_sg->GetNetName() );
881
882 if( sg_to_scan.empty() )
883 {
884 wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld with net %s has no neighbors" ),
885 aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName() );
886 sg_to_scan.push_back( item_sg );
887 }
888
889 wxLogTrace( ConnTrace,
890 wxT( "Removing all item %s connections from subgraph %ld with net %s: Found "
891 "%zu subgraphs" ),
892 aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName(),
893 sg_to_scan.size() );
894
895 for( CONNECTION_SUBGRAPH* sg : sg_to_scan )
896
897 {
898 traverse_subgraph( sg );
899
900 for( auto& bus_it : sg->m_bus_neighbors )
901 {
902 for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
903 traverse_subgraph( bus_sg );
904 }
905
906 for( auto& bus_it : sg->m_bus_parents )
907 {
908 for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
909 traverse_subgraph( bus_sg );
910 }
911 }
912
914 };
915
916 for( SCH_ITEM* item : aItems )
917 {
918 if( item->Type() == SCH_SHEET_T )
919 {
920 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
921
922 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
923 extract_element( pin );
924 }
925 else if ( item->Type() == SCH_SYMBOL_T )
926 {
927 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
928
929 for( SCH_PIN* pin : symbol->GetPins( &m_schematic->CurrentSheet() ) )
930 extract_element( pin );
931 }
932 else
933 {
934 extract_element( item );
935 }
936 }
937
938 removeSubgraphs( subgraphs );
939
940 return retvals;
941}
942
943
945{
946 auto it = m_item_to_subgraph_map.find( aItem );
947
948 if( it == m_item_to_subgraph_map.end() )
949 return;
950
951 CONNECTION_SUBGRAPH* subgraph = it->second;
952
953 while(subgraph->m_absorbed_by )
954 subgraph = subgraph->m_absorbed_by;
955
956 subgraph->RemoveItem( aItem );
958 m_item_to_subgraph_map.erase( it );
959}
960
961
962void CONNECTION_GRAPH::removeSubgraphs( std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
963{
964 wxLogTrace( ConnTrace, wxT( "Removing %zu subgraphs" ), aSubgraphs.size() );
965 std::sort( m_driver_subgraphs.begin(), m_driver_subgraphs.end() );
966 std::sort( m_subgraphs.begin(), m_subgraphs.end() );
967 std::set<int> codes_to_remove;
968
969 for( auto& el : m_sheet_to_subgraphs_map )
970 {
971 std::sort( el.second.begin(), el.second.end() );
972 }
973
974 for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
975 {
976 for( auto& it : sg->m_bus_neighbors )
977 {
978 for( CONNECTION_SUBGRAPH* neighbor : it.second )
979 {
980 auto& parents = neighbor->m_bus_parents[it.first];
981
982 for( auto test = parents.begin(); test != parents.end(); )
983 {
984 if( *test == sg )
985 test = parents.erase( test );
986 else
987 ++test;
988 }
989
990 if( parents.empty() )
991 neighbor->m_bus_parents.erase( it.first );
992 }
993 }
994
995 for( auto& it : sg->m_bus_parents )
996 {
997 for( CONNECTION_SUBGRAPH* parent : it.second )
998 {
999 auto& neighbors = parent->m_bus_neighbors[it.first];
1000
1001 for( auto test = neighbors.begin(); test != neighbors.end(); )
1002 {
1003 if( *test == sg )
1004 test = neighbors.erase( test );
1005 else
1006 ++test;
1007 }
1008
1009 if( neighbors.empty() )
1010 parent->m_bus_neighbors.erase( it.first );
1011 }
1012 }
1013
1014 {
1015 auto it = std::lower_bound( m_driver_subgraphs.begin(), m_driver_subgraphs.end(), sg );
1016
1017 while( it != m_driver_subgraphs.end() && *it == sg )
1018 it = m_driver_subgraphs.erase( it );
1019 }
1020
1021 {
1022 auto it = std::lower_bound( m_subgraphs.begin(), m_subgraphs.end(), sg );
1023
1024 while( it != m_subgraphs.end() && *it == sg )
1025 it = m_subgraphs.erase( it );
1026 }
1027
1028 for( auto& el : m_sheet_to_subgraphs_map )
1029 {
1030 auto it = std::lower_bound( el.second.begin(), el.second.end(), sg );
1031
1032 while( it != el.second.end() && *it == sg )
1033 it = el.second.erase( it );
1034 }
1035
1036 auto remove_sg = [sg]( auto it ) -> bool
1037 {
1038 for( const CONNECTION_SUBGRAPH* test_sg : it->second )
1039 {
1040 if( sg == test_sg )
1041 return true;
1042 }
1043
1044 return false;
1045 };
1046
1047 for( auto it = m_global_label_cache.begin(); it != m_global_label_cache.end(); )
1048 {
1049 if( remove_sg( it ) )
1050 it = m_global_label_cache.erase( it );
1051 else
1052 ++it;
1053 }
1054
1055 for( auto it = m_local_label_cache.begin(); it != m_local_label_cache.end(); )
1056 {
1057 if( remove_sg( it ) )
1058 it = m_local_label_cache.erase( it );
1059 else
1060 ++it;
1061 }
1062
1063 for( auto it = m_net_code_to_subgraphs_map.begin();
1064 it != m_net_code_to_subgraphs_map.end(); )
1065 {
1066 if( remove_sg( it ) )
1067 {
1068 codes_to_remove.insert( it->first.Netcode );
1069 it = m_net_code_to_subgraphs_map.erase( it );
1070 }
1071 else
1072 ++it;
1073 }
1074
1075 for( auto it = m_net_name_to_subgraphs_map.begin();
1076 it != m_net_name_to_subgraphs_map.end(); )
1077 {
1078 if( remove_sg( it ) )
1079 it = m_net_name_to_subgraphs_map.erase( it );
1080 else
1081 ++it;
1082 }
1083
1084 for( auto it = m_item_to_subgraph_map.begin(); it != m_item_to_subgraph_map.end(); )
1085 {
1086 if( it->second == sg )
1087 it = m_item_to_subgraph_map.erase( it );
1088 else
1089 ++it;
1090 }
1091
1092
1093 }
1094
1095 for( auto it = m_net_name_to_code_map.begin(); it != m_net_name_to_code_map.end(); )
1096 {
1097 if( codes_to_remove.contains( it->second ) )
1098 it = m_net_name_to_code_map.erase( it );
1099 else
1100 ++it;
1101 }
1102
1103 for( auto it = m_bus_name_to_code_map.begin(); it != m_bus_name_to_code_map.end(); )
1104 {
1105 if( codes_to_remove.contains( it->second ) )
1106 it = m_bus_name_to_code_map.erase( it );
1107 else
1108 ++it;
1109 }
1110
1111 for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
1112 {
1113 sg->m_code = -1;
1114 sg->m_graph = nullptr;
1115 delete sg;
1116 }
1117}
1118
1119
1121 const std::vector<SCH_ITEM*>& aItemList )
1122{
1123 wxLogTrace( wxT( "Updating connectivity for sheet %s with %zu items" ),
1124 aSheet.Last()->GetFileName(), aItemList.size() );
1125 std::map<VECTOR2I, std::vector<SCH_ITEM*>> connection_map;
1126
1127 auto updatePin = [&]( SCH_PIN* aPin, SCH_CONNECTION* aConn )
1128 {
1129 aConn->SetType( CONNECTION_TYPE::NET );
1130
1131 // because calling the first time is not thread-safe
1132 wxString name = aPin->GetDefaultNetName( aSheet );
1133 aPin->ClearConnectedItems( aSheet );
1134
1135 // power symbol pins need to be post-processed later
1136 if( aPin->IsGlobalPower() )
1137 {
1138 aConn->SetName( name );
1139 m_global_power_pins.emplace_back( std::make_pair( aSheet, aPin ) );
1140 }
1141 };
1142
1143 for( SCH_ITEM* item : aItemList )
1144 {
1145 std::vector<VECTOR2I> points = item->GetConnectionPoints();
1146 item->ClearConnectedItems( aSheet );
1147
1148 if( item->Type() == SCH_SHEET_T )
1149 {
1150 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
1151 {
1152 pin->InitializeConnection( aSheet, this );
1153
1154 pin->ClearConnectedItems( aSheet );
1155
1156 connection_map[ pin->GetTextPos() ].push_back( pin );
1157 m_items.emplace_back( pin );
1158 }
1159 }
1160 else if( item->Type() == SCH_SYMBOL_T )
1161 {
1162 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1163
1164 for( SCH_PIN* pin : symbol->GetPins( &aSheet ) )
1165 {
1166 m_items.emplace_back( pin );
1167 SCH_CONNECTION* conn = pin->InitializeConnection( aSheet, this );
1168 updatePin( pin, conn );
1169 connection_map[ pin->GetPosition() ].push_back( pin );
1170 }
1171 }
1172 else
1173 {
1174 m_items.emplace_back( item );
1175 SCH_CONNECTION* conn = item->InitializeConnection( aSheet, this );
1176
1177 // Set bus/net property here so that the propagation code uses it
1178 switch( item->Type() )
1179 {
1180 case SCH_LINE_T:
1181 conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
1182 CONNECTION_TYPE::NET );
1183 break;
1184
1186 conn->SetType( CONNECTION_TYPE::BUS );
1187
1188 // clean previous (old) links:
1189 static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
1190 static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
1191 break;
1192
1193 case SCH_PIN_T:
1194 if( points.empty() )
1195 points = { static_cast<SCH_PIN*>( item )->GetPosition() };
1196
1197 updatePin( static_cast<SCH_PIN*>( item ), conn );
1198 break;
1199
1201 conn->SetType( CONNECTION_TYPE::NET );
1202
1203 // clean previous (old) link:
1204 static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
1205 break;
1206
1207 default:
1208 break;
1209 }
1210
1211 for( const VECTOR2I& point : points )
1212 connection_map[ point ].push_back( item );
1213 }
1214 }
1215
1216 for( const auto& it : connection_map )
1217 {
1218 std::vector<SCH_ITEM*> connection_vec = it.second;
1219 std::sort( connection_vec.begin(), connection_vec.end() );
1220 alg::remove_duplicates( connection_vec );
1221
1222 // Pre-scan to see if we have a bus at this location
1223 SCH_LINE* busLine = aSheet.LastScreen()->GetBus( it.first );
1224
1225 std::mutex update_mutex;
1226
1227 auto update_lambda = [&]( SCH_ITEM* connected_item ) -> size_t
1228 {
1229 // Bus entries are special: they can have connection points in the
1230 // middle of a wire segment, because the junction algo doesn't split
1231 // the segment in two where you place a bus entry. This means that
1232 // bus entries that don't land on the end of a line segment need to
1233 // have "virtual" connection points to the segments they graphically
1234 // touch.
1235 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1236 {
1237 // If this location only has the connection point of the bus
1238 // entry itself, this means that either the bus entry is not
1239 // connected to anything graphically, or that it is connected to
1240 // a segment at some point other than at one of the endpoints.
1241 if( connection_vec.size() == 1 )
1242 {
1243 if( busLine )
1244 {
1245 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1246 bus_entry->m_connected_bus_item = busLine;
1247 }
1248 }
1249 }
1250
1251 // Bus-to-bus entries are treated just like bus wires
1252 else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
1253 {
1254 if( connection_vec.size() < 2 )
1255 {
1256 if( busLine )
1257 {
1258 auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
1259
1260 if( it.first == bus_entry->GetPosition() )
1261 bus_entry->m_connected_bus_items[0] = busLine;
1262 else
1263 bus_entry->m_connected_bus_items[1] = busLine;
1264
1265 std::lock_guard<std::mutex> lock( update_mutex );
1266 bus_entry->AddConnectionTo( aSheet, busLine );
1267 busLine->AddConnectionTo( aSheet, bus_entry );
1268 }
1269 }
1270 }
1271
1272 // Change junctions to be on bus junction layer if they are touching a bus
1273 else if( connected_item->Type() == SCH_JUNCTION_T )
1274 {
1275 connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
1276 }
1277
1278 for( SCH_ITEM* test_item : connection_vec )
1279 {
1280 bool bus_connection_ok = true;
1281
1282 if( test_item == connected_item )
1283 continue;
1284
1285 // Set up the link between the bus entry net and the bus
1286 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1287 {
1288 if( test_item->GetLayer() == LAYER_BUS )
1289 {
1290 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1291 bus_entry->m_connected_bus_item = test_item;
1292 }
1293 }
1294
1295 // Bus entries only connect to bus lines on the end that is touching a bus line.
1296 // If the user has overlapped another net line with the endpoint of the bus entry
1297 // where the entry connects to a bus, we don't want to short-circuit it.
1298 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1299 {
1300 bus_connection_ok = !busLine || test_item->GetLayer() == LAYER_BUS;
1301 }
1302 else if( test_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1303 {
1304 bus_connection_ok = !busLine || connected_item->GetLayer() == LAYER_BUS;
1305 }
1306
1307 if( connected_item->ConnectionPropagatesTo( test_item ) &&
1308 test_item->ConnectionPropagatesTo( connected_item ) &&
1309 bus_connection_ok )
1310 {
1311 connected_item->AddConnectionTo( aSheet, test_item );
1312 }
1313 }
1314
1315 // If we got this far and did not find a connected bus item for a bus entry,
1316 // we should do a manual scan in case there is a bus item on this connection
1317 // point but we didn't pick it up earlier because there is *also* a net item here.
1318 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1319 {
1320 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1321
1322 if( !bus_entry->m_connected_bus_item )
1323 {
1324 SCH_SCREEN* screen = aSheet.LastScreen();
1325 SCH_LINE* bus = screen->GetBus( it.first );
1326
1327 if( bus )
1328 bus_entry->m_connected_bus_item = bus;
1329 }
1330 }
1331
1332 return 1;
1333 };
1334
1336
1337 tp.push_loop( connection_vec.size(),
1338 [&]( const int a, const int b)
1339 {
1340 for( int ii = a; ii < b; ++ii )
1341 update_lambda( connection_vec[ii] );
1342 });
1343 tp.wait_for_tasks();
1344 }
1345}
1346
1347
1349{
1350 // Recache all bus aliases for later use
1351 wxCHECK_RET( m_schematic, wxS( "Connection graph cannot be built without schematic pointer" ) );
1352
1353 SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
1354
1355 for( unsigned i = 0; i < all_sheets.size(); i++ )
1356 {
1357 for( const std::shared_ptr<BUS_ALIAS>& alias : all_sheets[i].LastScreen()->GetBusAliases() )
1358 m_bus_alias_cache[ alias->GetName() ] = alias;
1359 }
1360
1361 // Build subgraphs from items (on a per-sheet basis)
1362 for( SCH_ITEM* item : m_items )
1363 {
1364 for( const auto& it : item->m_connection_map )
1365 {
1366 const SCH_SHEET_PATH& sheet = it.first;
1367 SCH_CONNECTION* connection = it.second;
1368
1369 if( connection->SubgraphCode() == 0 )
1370 {
1371 CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
1372
1373 subgraph->m_code = m_last_subgraph_code++;
1374 subgraph->m_sheet = sheet;
1375
1376 subgraph->AddItem( item );
1377
1378 connection->SetSubgraphCode( subgraph->m_code );
1379 m_item_to_subgraph_map[item] = subgraph;
1380
1381 std::list<SCH_ITEM*> memberlist;
1382
1383 auto get_items =
1384 [&]( SCH_ITEM* aItem ) -> bool
1385 {
1386 SCH_CONNECTION* conn = aItem->GetOrInitConnection( sheet, this );
1387 bool unique = !( aItem->GetFlags() & CANDIDATE );
1388
1389 if( conn && !conn->SubgraphCode() )
1390 aItem->SetFlags( CANDIDATE );
1391
1392 return ( unique && conn && ( conn->SubgraphCode() == 0 ) );
1393 };
1394
1395 std::copy_if( item->ConnectedItems( sheet ).begin(),
1396 item->ConnectedItems( sheet ).end(),
1397 std::back_inserter( memberlist ), get_items );
1398
1399 for( SCH_ITEM* connected_item : memberlist )
1400 {
1401 if( connected_item->Type() == SCH_NO_CONNECT_T )
1402 subgraph->m_no_connect = connected_item;
1403
1404 SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
1405
1406 wxASSERT( connected_conn );
1407
1408 if( connected_conn->SubgraphCode() == 0 )
1409 {
1410 connected_conn->SetSubgraphCode( subgraph->m_code );
1411 m_item_to_subgraph_map[connected_item] = subgraph;
1412 subgraph->AddItem( connected_item );
1413 const SCH_ITEM_VEC& citemset = connected_item->ConnectedItems( sheet );
1414
1415 for( SCH_ITEM* citem : citemset )
1416 {
1417 if( citem->HasFlag( CANDIDATE ) )
1418 continue;
1419
1420 if( get_items( citem ) )
1421 memberlist.push_back( citem );
1422 }
1423 }
1424 }
1425
1426 for( SCH_ITEM* connected_item : memberlist )
1427 connected_item->ClearFlags( CANDIDATE );
1428
1429 subgraph->m_dirty = true;
1430 m_subgraphs.push_back( subgraph );
1431 }
1432 }
1433 }
1434
1435}
1436
1438{
1439 // Resolve drivers for subgraphs and propagate connectivity info
1440 std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
1441
1442 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
1443 [&] ( const CONNECTION_SUBGRAPH* candidate )
1444 {
1445 return candidate->m_dirty;
1446 } );
1447
1448 wxLogTrace( ConnTrace, wxT( "Resolving drivers for %zu subgraphs" ), dirty_graphs.size() );
1449
1450 std::vector<std::future<size_t>> returns( dirty_graphs.size() );
1451
1452 auto update_lambda = []( CONNECTION_SUBGRAPH* subgraph ) -> size_t
1453 {
1454 if( !subgraph->m_dirty )
1455 return 0;
1456
1457 // Special processing for some items
1458 for( SCH_ITEM* item : subgraph->m_items )
1459 {
1460 switch( item->Type() )
1461 {
1462 case SCH_NO_CONNECT_T:
1463 subgraph->m_no_connect = item;
1464 break;
1465
1467 subgraph->m_bus_entry = item;
1468 break;
1469
1470 case SCH_PIN_T:
1471 {
1472 auto pin = static_cast<SCH_PIN*>( item );
1473
1474 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
1475 subgraph->m_no_connect = item;
1476
1477 break;
1478 }
1479
1480 default:
1481 break;
1482 }
1483 }
1484
1485 subgraph->ResolveDrivers( true );
1486 subgraph->m_dirty = false;
1487
1488 return 1;
1489 };
1490
1492
1493 tp.push_loop( dirty_graphs.size(),
1494 [&]( const int a, const int b)
1495 {
1496 for( int ii = a; ii < b; ++ii )
1497 update_lambda( dirty_graphs[ii] );
1498 });
1499 tp.wait_for_tasks();
1500
1501 // Now discard any non-driven subgraphs from further consideration
1502
1503 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
1504 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1505 {
1506 return candidate->m_driver;
1507 } );
1508}
1509
1510
1512{
1513 // Check for subgraphs with the same net name but only weak drivers.
1514 // For example, two wires that are both connected to hierarchical
1515 // sheet pins that happen to have the same name, but are not the same.
1516
1517 for( auto&& subgraph : m_driver_subgraphs )
1518 {
1519 wxString full_name = subgraph->m_driver_connection->Name();
1520 wxString name = subgraph->m_driver_connection->Name( true );
1521 m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
1522
1523 // For vector buses, we need to cache the prefix also, as two different instances of the
1524 // weakly driven pin may have the same prefix but different vector start and end. We need
1525 // to treat those as needing renaming also, because otherwise if they end up on a sheet with
1526 // common usage, they will be incorrectly merged.
1527 if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1528 {
1529 wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
1530 m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
1531 }
1532
1533 subgraph->m_dirty = true;
1534
1535 if( subgraph->m_strong_driver )
1536 {
1537 SCH_ITEM* driver = subgraph->m_driver;
1538 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1539
1540 switch( driver->Type() )
1541 {
1542 case SCH_LABEL_T:
1543 case SCH_HIER_LABEL_T:
1544 {
1545 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1546 break;
1547 }
1548 case SCH_GLOBAL_LABEL_T:
1549 {
1550 m_global_label_cache[name].push_back( subgraph );
1551 break;
1552 }
1553 case SCH_PIN_T:
1554 {
1555 SCH_PIN* pin = static_cast<SCH_PIN*>( driver );
1556 wxASSERT( pin->IsGlobalPower() );
1557 m_global_label_cache[name].push_back( subgraph );
1558 break;
1559 }
1560 default:
1561 {
1562 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MILLIMETRES );
1563
1564 wxLogTrace( ConnTrace, wxS( "Unexpected strong driver %s" ),
1565 driver->GetItemDescription( &unitsProvider ) );
1566 break;
1567 }
1568 }
1569 }
1570 }
1571}
1572
1573
1575{
1576 std::vector<CONNECTION_SUBGRAPH*> new_subgraphs;
1577
1578 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1579 {
1580 SCH_ITEM_VEC vec = subgraph->GetAllBusLabels();
1581
1582 for( SCH_ITEM* item : vec )
1583 {
1584 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
1585
1586 SCH_CONNECTION dummy( item, subgraph->m_sheet );
1587 dummy.SetGraph( this );
1588 dummy.ConfigureFromLabel( label->GetText() );
1589
1590 wxLogTrace( ConnTrace, wxS( "new bus label (%s)" ), label->GetText() );
1591
1592 for( const auto& conn : dummy.Members() )
1593 {
1594 wxString name = conn->FullLocalName();
1595
1596 CONNECTION_SUBGRAPH* new_sg = new CONNECTION_SUBGRAPH( this );
1597
1598 // This connection cannot form a part of the item because the item is not, itself
1599 // connected to this subgraph. It exists as part of a virtual item that may be
1600 // connected to other items but is not in the schematic.
1601 SCH_CONNECTION* new_conn = new SCH_CONNECTION( item, subgraph->m_sheet );
1602 new_conn->SetGraph( this );
1603 new_conn->SetName( name );
1604 new_conn->SetType( CONNECTION_TYPE::NET );
1605 subgraph->StoreImplicitConnection( new_conn );
1606 int code = assignNewNetCode( *new_conn );
1607
1608 wxLogTrace( ConnTrace, wxS( "SG(%ld), Adding full local name (%s) with sg (%d) "
1609 "on subsheet %s" ),
1610 subgraph->m_code, name, code, subgraph->m_sheet.PathHumanReadable() );
1611
1612 new_sg->m_driver_connection = new_conn;
1613 new_sg->m_code = m_last_subgraph_code++;
1614 new_sg->m_sheet = subgraph->GetSheet();
1615 new_sg->m_is_bus_member = true;
1616 new_sg->m_strong_driver = true;
1617
1619 NET_NAME_CODE_CACHE_KEY key = { new_sg->GetNetName(), code };
1620 m_net_code_to_subgraphs_map[ key ].push_back( new_sg );
1621 m_net_name_to_subgraphs_map[ name ].push_back( new_sg );
1622 m_subgraphs.push_back( new_sg );
1623 new_subgraphs.push_back( new_sg );
1624 }
1625 }
1626 }
1627
1628 std::copy( new_subgraphs.begin(), new_subgraphs.end(),
1629 std::back_inserter( m_driver_subgraphs ) );
1630}
1631
1633{
1634 // Generate subgraphs for global power pins. These will be merged with other subgraphs
1635 // on the same sheet in the next loop.
1636 // These are NOT limited to power symbols, we support legacy invisible + power-in pins
1637 // on non-power symbols.
1638
1639 std::unordered_map<int, CONNECTION_SUBGRAPH*> global_power_pin_subgraphs;
1640
1641 for( const auto& it : m_global_power_pins )
1642 {
1643 SCH_SHEET_PATH sheet = it.first;
1644 SCH_PIN* pin = it.second;
1645
1646 if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParentSymbol()->IsPower() )
1647 {
1648 // ERC will warn about this: user has wired up an invisible pin
1649 continue;
1650 }
1651
1652 SCH_CONNECTION* connection = pin->GetOrInitConnection( sheet, this );
1653
1654 // If this pin already has a subgraph, don't need to process
1655 if( !connection || connection->SubgraphCode() > 0 )
1656 continue;
1657
1658 // Proper modern power symbols get their net name from the value field
1659 // in the symbol, but we support legacy non-power symbols with global
1660 // power connections based on invisible, power-in, pin's names.
1661 if( pin->GetLibPin()->GetParentSymbol()->IsPower() )
1662 connection->SetName( pin->GetParentSymbol()->GetValue( true, &sheet, false ) );
1663 else
1664 connection->SetName( pin->GetShownName() );
1665
1666 int code = assignNewNetCode( *connection );
1667
1668 connection->SetNetCode( code );
1669
1670 CONNECTION_SUBGRAPH* subgraph;
1671 auto jj = global_power_pin_subgraphs.find( code );
1672
1673 if( jj != global_power_pin_subgraphs.end() )
1674 {
1675 subgraph = jj->second;
1676 subgraph->AddItem( pin );
1677 }
1678 else
1679 {
1680 subgraph = new CONNECTION_SUBGRAPH( this );
1681
1682 subgraph->m_code = m_last_subgraph_code++;
1683 subgraph->m_sheet = sheet;
1684
1685 subgraph->AddItem( pin );
1686 subgraph->ResolveDrivers();
1687
1688 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(), code };
1689 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1690 m_subgraphs.push_back( subgraph );
1691 m_driver_subgraphs.push_back( subgraph );
1692
1693 global_power_pin_subgraphs[code] = subgraph;
1694 }
1695
1696 connection->SetSubgraphCode( subgraph->m_code );
1697 }
1698}
1699
1700
1702{
1703 // Here we do all the local (sheet) processing of each subgraph, including assigning net
1704 // codes, merging subgraphs together that use label connections, etc.
1705
1706 // Cache remaining valid subgraphs by sheet path
1707 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1708 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1709
1710 std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
1711
1712 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1713 {
1714 if( subgraph->m_absorbed )
1715 continue;
1716
1717 SCH_CONNECTION* connection = subgraph->m_driver_connection;
1718 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1719 wxString name = connection->Name();
1720
1721 // Test subgraphs with weak drivers for net name conflicts and fix them
1722 unsigned suffix = 1;
1723
1724 auto create_new_name =
1725 [&suffix]( SCH_CONNECTION* aConn ) -> wxString
1726 {
1727 wxString newName;
1728 wxString suffixStr = std::to_wstring( suffix );
1729
1730 // For group buses with a prefix, we can add the suffix to the prefix.
1731 // If they don't have a prefix, we force the creation of a prefix so that
1732 // two buses don't get inadvertently shorted together.
1733 if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
1734 {
1735 wxString prefix = aConn->BusPrefix();
1736
1737 if( prefix.empty() )
1738 prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
1739
1740 wxString oldName = aConn->Name().AfterFirst( '{' );
1741
1742 newName << prefix << wxT( "_" ) << suffixStr << wxT( "{" ) << oldName;
1743
1744 aConn->ConfigureFromLabel( newName );
1745 }
1746 else
1747 {
1748 newName << aConn->Name() << wxT( "_" ) << suffixStr;
1749 aConn->SetSuffix( wxString( wxT( "_" ) ) << suffixStr );
1750 }
1751
1752 suffix++;
1753 return newName;
1754 };
1755
1756 if( !subgraph->m_strong_driver )
1757 {
1758 std::vector<CONNECTION_SUBGRAPH*> vec_empty;
1759 std::vector<CONNECTION_SUBGRAPH*>* vec = &vec_empty;
1760
1761 if( m_net_name_to_subgraphs_map.count( name ) )
1762 vec = &m_net_name_to_subgraphs_map.at( name );
1763
1764 // If we are a unique bus vector, check if we aren't actually unique because of another
1765 // subgraph with a similar bus vector
1766 if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1767 {
1768 wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
1769
1770 if( m_net_name_to_subgraphs_map.count( prefixOnly ) )
1771 vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
1772 }
1773
1774 if( vec->size() > 1 )
1775 {
1776 wxString new_name = create_new_name( connection );
1777
1778 while( m_net_name_to_subgraphs_map.contains( new_name ) )
1779 new_name = create_new_name( connection );
1780
1781 wxLogTrace( ConnTrace,
1782 wxS( "%ld (%s) is weakly driven and not unique. Changing to %s." ),
1783 subgraph->m_code, name, new_name );
1784
1785 alg::delete_matching( *vec, subgraph );
1786
1787 m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1788
1789 name = new_name;
1790 }
1791 else if( subgraph->m_driver )
1792 {
1793 // If there is no conflict, promote sheet pins to be strong drivers so that they
1794 // will be considered below for propagation/merging.
1795
1796 // It is possible for this to generate a conflict if the sheet pin has the same
1797 // name as a global label on the same sheet, because global merging will then treat
1798 // this subgraph as if it had a matching local label. So, for those cases, we
1799 // don't apply this promotion
1800
1801 if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1802 {
1803 bool conflict = false;
1804 wxString global_name = connection->Name( true );
1805 auto kk = m_net_name_to_subgraphs_map.find( global_name );
1806
1807 if( kk != m_net_name_to_subgraphs_map.end() )
1808 {
1809 // A global will conflict if it is on the same sheet as this subgraph, since
1810 // it would be connected by implicit local label linking
1811 std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
1812
1813 for( const CONNECTION_SUBGRAPH* candidate : candidates )
1814 {
1815 if( candidate->m_sheet == sheet )
1816 conflict = true;
1817 }
1818 }
1819
1820 if( conflict )
1821 {
1822 wxLogTrace( ConnTrace,
1823 wxS( "%ld (%s) skipped for promotion due to potential "
1824 "conflict" ),
1825 subgraph->m_code, name );
1826 }
1827 else
1828 {
1829 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MILLIMETRES );
1830
1831 wxLogTrace( ConnTrace,
1832 wxS( "%ld (%s) weakly driven by unique sheet pin %s, "
1833 "promoting" ),
1834 subgraph->m_code, name,
1835 subgraph->m_driver->GetItemDescription( &unitsProvider ) );
1836
1837 subgraph->m_strong_driver = true;
1838 }
1839 }
1840 }
1841 }
1842
1843 // Assign net codes
1844 if( connection->IsBus() )
1845 {
1846 int code = -1;
1847 auto it = m_bus_name_to_code_map.find( name );
1848
1849 if( it != m_bus_name_to_code_map.end() )
1850 {
1851 code = it->second;
1852 }
1853 else
1854 {
1855 code = m_last_bus_code++;
1856 m_bus_name_to_code_map[ name ] = code;
1857 }
1858
1859 connection->SetBusCode( code );
1860 assignNetCodesToBus( connection );
1861 }
1862 else
1863 {
1864 assignNewNetCode( *connection );
1865 }
1866
1867 // Reset the flag for the next loop below
1868 subgraph->m_dirty = true;
1869
1870 // Next, we merge together subgraphs that have label connections, and create
1871 // neighbor links for subgraphs that are part of a bus on the same sheet.
1872 // For merging, we consider each possible strong driver.
1873
1874 // If this subgraph doesn't have a strong driver, let's skip it, since there is no
1875 // way it will be merged with anything.
1876 if( !subgraph->m_strong_driver )
1877 continue;
1878
1879 // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
1880 // as the subgraph we are considering that has a strong driver.
1881 // Weakly driven subgraphs are not considered since they will never be absorbed or
1882 // form neighbor links.
1883 std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
1884 std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
1885 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
1886 std::back_inserter( candidate_subgraphs ),
1887 [&] ( const CONNECTION_SUBGRAPH* candidate )
1888 {
1889 return ( !candidate->m_absorbed &&
1890 candidate->m_strong_driver &&
1891 candidate != subgraph );
1892 } );
1893
1894 // This is a list of connections on the current subgraph to compare to the
1895 // drivers of each candidate subgraph. If the current subgraph is a bus,
1896 // we should consider each bus member.
1897 std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
1898
1899 // Also check the main driving connection
1900 connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
1901
1902 auto add_connections_to_check =
1903 [&] ( CONNECTION_SUBGRAPH* aSubgraph )
1904 {
1905 for( SCH_ITEM* possible_driver : aSubgraph->m_items )
1906 {
1907 if( possible_driver == aSubgraph->m_driver )
1908 continue;
1909
1910 auto c = getDefaultConnection( possible_driver, aSubgraph );
1911
1912 if( c )
1913 {
1914 if( c->Type() != aSubgraph->m_driver_connection->Type() )
1915 continue;
1916
1917 if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
1918 continue;
1919
1920 connections_to_check.push_back( c );
1921 wxLogTrace( ConnTrace,
1922 wxS( "%lu (%s): Adding secondary driver %s" ),
1923 aSubgraph->m_code,
1924 aSubgraph->m_driver_connection->Name( true ),
1925 c->Name( true ) );
1926 }
1927 }
1928 };
1929
1930 // Now add other strong drivers
1931 // The actual connection attached to these items will have been overwritten
1932 // by the chosen driver of the subgraph, so we need to create a dummy connection
1933 add_connections_to_check( subgraph );
1934
1935 std::set<SCH_CONNECTION*> checked_connections;
1936
1937 for( unsigned i = 0; i < connections_to_check.size(); i++ )
1938 {
1939 auto member = connections_to_check[i];
1940
1941 // Don't check the same connection twice
1942 if( !checked_connections.insert( member.get() ).second )
1943 continue;
1944
1945 if( member->IsBus() )
1946 {
1947 connections_to_check.insert( connections_to_check.end(),
1948 member->Members().begin(),
1949 member->Members().end() );
1950 }
1951
1952 wxString test_name = member->Name( true );
1953
1954 for( CONNECTION_SUBGRAPH* candidate : candidate_subgraphs )
1955 {
1956 if( candidate->m_absorbed || candidate == subgraph )
1957 continue;
1958
1959 bool match = false;
1960
1961 if( candidate->m_driver_connection->Name( true ) == test_name )
1962 {
1963 match = true;
1964 }
1965 else
1966 {
1967 if( !candidate->m_multiple_drivers )
1968 continue;
1969
1970 for( SCH_ITEM *driver : candidate->m_drivers )
1971 {
1972 if( driver == candidate->m_driver )
1973 continue;
1974
1975 // Sheet pins are not candidates for merging
1976 if( driver->Type() == SCH_SHEET_PIN_T )
1977 continue;
1978
1979 if( driver->Type() == SCH_PIN_T )
1980 {
1981 auto pin = static_cast<SCH_PIN*>( driver );
1982
1983 if( pin->IsGlobalPower()
1984 && pin->GetDefaultNetName( sheet ) == test_name )
1985 {
1986 match = true;
1987 break;
1988 }
1989 }
1990 else
1991 {
1992 wxASSERT( driver->Type() == SCH_LABEL_T ||
1993 driver->Type() == SCH_GLOBAL_LABEL_T ||
1994 driver->Type() == SCH_HIER_LABEL_T );
1995
1996 if( subgraph->GetNameForDriver( driver ) == test_name )
1997 {
1998 match = true;
1999 break;
2000 }
2001 }
2002 }
2003 }
2004
2005 if( match )
2006 {
2007 if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
2008 {
2009 wxLogTrace( ConnTrace, wxS( "%lu (%s) has bus child %lu (%s)" ),
2010 subgraph->m_code, connection->Name(),
2011 candidate->m_code, member->Name() );
2012
2013 subgraph->m_bus_neighbors[member].insert( candidate );
2014 candidate->m_bus_parents[member].insert( subgraph );
2015 }
2016 else if( connection->Type() == candidate->m_driver_connection->Type() )
2017 {
2018 wxLogTrace( ConnTrace, wxS( "%lu (%s) absorbs neighbor %lu (%s)" ),
2019 subgraph->m_code, connection->Name(),
2020 candidate->m_code, candidate->m_driver_connection->Name() );
2021
2022 // Candidate may have other non-chosen drivers we need to follow
2023 add_connections_to_check( candidate );
2024
2025 subgraph->Absorb( candidate );
2026 invalidated_subgraphs.insert( subgraph );
2027 }
2028 }
2029 }
2030 }
2031 }
2032
2033 // Update any subgraph that was invalidated above
2034 for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
2035 {
2036 if( subgraph->m_absorbed )
2037 continue;
2038
2039 if( !subgraph->ResolveDrivers() )
2040 continue;
2041
2042 if( subgraph->m_driver_connection->IsBus() )
2043 assignNetCodesToBus( subgraph->m_driver_connection );
2044 else
2045 assignNewNetCode( *subgraph->m_driver_connection );
2046
2047 wxLogTrace( ConnTrace, wxS( "Re-resolving drivers for %lu (%s)" ), subgraph->m_code,
2048 subgraph->m_driver_connection->Name() );
2049 }
2050
2051}
2052
2053
2054// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
2055// to the same subgraph necessarily if it runs over and over again on the same
2056// sheet. We need:
2057//
2058// a) a cache of net/bus codes, like used before
2059// b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
2060// c) some way of trying to avoid changing net names. so we should keep track
2061// of the previous driver of a net, and if it comes down to choosing between
2062// equally-prioritized drivers, choose the one that already exists as a driver
2063// on some portion of the items.
2064
2065
2066void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* aChangedItemHandler, bool aUnconditional )
2067{
2068 // Recache all bus aliases for later use
2069 wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) );
2070
2071 SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
2072
2073 for( unsigned i = 0; i < all_sheets.size(); i++ )
2074 {
2075 for( const std::shared_ptr<BUS_ALIAS>& alias : all_sheets[i].LastScreen()->GetBusAliases() )
2076 m_bus_alias_cache[ alias->GetName() ] = alias;
2077 }
2078
2079 PROF_TIMER sub_graph( "buildItemSubGraphs" );
2081
2082 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2083 sub_graph.Show();
2084
2085
2092
2094
2096
2098
2099 PROF_TIMER proc_sub_graph( "ProcessSubGraphs" );
2101
2102 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2103 proc_sub_graph.Show();
2104
2105 // Absorbed subgraphs should no longer be considered
2106 alg::delete_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool
2107 {
2108 return candidate->m_absorbed;
2109 } );
2110
2111 // Store global subgraphs for later reference
2112 std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
2113 std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
2114 std::back_inserter( global_subgraphs ),
2115 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
2116 {
2117 return !candidate->m_local_driver;
2118 } );
2119
2120 // Recache remaining valid subgraphs by sheet path
2122
2123 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2124 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
2125
2127
2128 tp.push_loop( m_driver_subgraphs.size(),
2129 [&]( const int a, const int b)
2130 {
2131 for( int ii = a; ii < b; ++ii )
2132 m_driver_subgraphs[ii]->UpdateItemConnections();
2133 });
2134 tp.wait_for_tasks();
2135
2136 // Next time through the subgraphs, we do some post-processing to handle things like
2137 // connecting bus members to their neighboring subgraphs, and then propagate connections
2138 // through the hierarchy
2139 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2140 {
2141 if( !subgraph->m_dirty )
2142 continue;
2143
2144 wxLogTrace( ConnTrace, wxS( "Processing %lu (%s) for propagation" ), subgraph->m_code,
2145 subgraph->m_driver_connection->Name() );
2146
2147 // For subgraphs that are driven by a global (power port or label) and have more
2148 // than one global driver, we need to seek out other subgraphs driven by the
2149 // same name as the non-chosen driver and update them to match the chosen one.
2150
2151 if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
2152 {
2153 for( SCH_ITEM* driver : subgraph->m_drivers )
2154 {
2155 if( driver == subgraph->m_driver )
2156 continue;
2157
2158 const wxString& secondary_name = subgraph->GetNameForDriver( driver );
2159
2160 if( secondary_name == subgraph->m_driver_connection->Name() )
2161 continue;
2162
2163 bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
2165
2166 for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
2167 {
2168 if( candidate == subgraph )
2169 continue;
2170
2171 if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
2172 continue;
2173
2174 SCH_CONNECTION* conn = candidate->m_driver_connection;
2175
2176 if( conn->Name() == secondary_name )
2177 {
2178 wxLogTrace( ConnTrace, wxS( "Global %lu (%s) promoted to %s" ),
2179 candidate->m_code, conn->Name(),
2180 subgraph->m_driver_connection->Name() );
2181
2182 conn->Clone( *subgraph->m_driver_connection );
2183
2184 candidate->m_dirty = false;
2185 propagateToNeighbors( candidate, false );
2186 }
2187 }
2188 }
2189 }
2190
2191 // This call will handle descending the hierarchy and updating child subgraphs
2192 propagateToNeighbors( subgraph, false );
2193 }
2194
2195 // After processing and allowing some to be skipped if they have hierarchical
2196 // pins connecting both up and down the hierarchy, we check to see if any of them
2197 // have not been processed. This would indicate that they do not have off-sheet connections
2198 // but we still need to handle the subgraph
2199 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2200 {
2201 if( subgraph->m_dirty )
2202 propagateToNeighbors( subgraph, true );
2203 }
2204
2205 // Handle buses that have been linked together somewhere by member (net) connections.
2206 // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
2207
2208 // For net subgraphs that have more than one bus parent, we need to ensure that those
2209 // buses are linked together in the final netlist. The final name of each bus might not
2210 // match the local name that was used to establish the parent-child relationship, because
2211 // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
2212 // we need to identify the appropriate bus members to link together (and their final names),
2213 // and then update all instances of the old name in the hierarchy.
2214 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2215 {
2216 // All SGs should have been processed by propagateToNeighbors above
2217 wxASSERT_MSG( !subgraph->m_dirty,
2218 wxS( "Subgraph not processed by propagateToNeighbors!" ) );
2219
2220 if( subgraph->m_bus_parents.size() < 2 )
2221 continue;
2222
2223 SCH_CONNECTION* conn = subgraph->m_driver_connection;
2224
2225 wxLogTrace( ConnTrace, wxS( "%lu (%s) has multiple bus parents" ),
2226 subgraph->m_code, conn->Name() );
2227
2228 wxASSERT( conn->IsNet() );
2229
2230 for( const auto& ii : subgraph->m_bus_parents )
2231 {
2232 SCH_CONNECTION* link_member = ii.first.get();
2233
2234 for( CONNECTION_SUBGRAPH* parent : ii.second )
2235 {
2236 while( parent->m_absorbed )
2237 parent = parent->m_absorbed_by;
2238
2239 SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
2240
2241 if( !match )
2242 {
2243 wxLogTrace( ConnTrace, wxS( "Warning: could not match %s inside %lu (%s)" ),
2244 conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
2245 continue;
2246 }
2247
2248 if( conn->Name() != match->Name() )
2249 {
2250 wxString old_name = match->Name();
2251
2252 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ),
2253 parent->m_code, parent->m_driver_connection->Name(),
2254 old_name, conn->Name() );
2255
2256 match->Clone( *conn );
2257
2258 auto jj = m_net_name_to_subgraphs_map.find( old_name );
2259
2260 if( jj == m_net_name_to_subgraphs_map.end() )
2261 continue;
2262
2263 for( CONNECTION_SUBGRAPH* old_sg : jj->second )
2264 {
2265 while( old_sg->m_absorbed )
2266 old_sg = old_sg->m_absorbed_by;
2267
2268 old_sg->m_driver_connection->Clone( *conn );
2269 }
2270 }
2271 }
2272 }
2273 }
2274
2275 auto updateItemConnectionsTask =
2276 [&]( CONNECTION_SUBGRAPH* subgraph ) -> size_t
2277 {
2278 // Make sure weakly-driven single-pin nets get the unconnected_ prefix
2279 if( !subgraph->m_strong_driver && subgraph->m_drivers.size() == 1 &&
2280 subgraph->m_driver->Type() == SCH_PIN_T )
2281 {
2282 SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
2283 wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
2284
2285 subgraph->m_driver_connection->ConfigureFromLabel( name );
2286 }
2287
2288 subgraph->m_dirty = false;
2289 subgraph->UpdateItemConnections();
2290
2291 // No other processing to do on buses
2292 if( subgraph->m_driver_connection->IsBus() )
2293 return 0;
2294
2295 // As a visual aid, we can check sheet pins that are driven by themselves to see
2296 // if they should be promoted to buses
2297 if( subgraph->m_driver && subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
2298 {
2299 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
2300
2301 if( SCH_SHEET* sheet = pin->GetParent() )
2302 {
2303 wxString pinText = pin->GetText();
2304 SCH_SCREEN* screen = sheet->GetScreen();
2305
2306 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
2307 {
2308 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
2309
2310 if( label->GetText() == pinText )
2311 {
2312 SCH_SHEET_PATH path = subgraph->m_sheet;
2313 path.push_back( sheet );
2314
2315 SCH_CONNECTION* parent_conn = label->Connection( &path );
2316
2317 if( parent_conn && parent_conn->IsBus() )
2318 subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
2319
2320 break;
2321 }
2322 }
2323
2324 if( subgraph->m_driver_connection->IsBus() )
2325 return 0;
2326 }
2327 }
2328
2329 return 1;
2330 };
2331
2332 tp.push_loop( m_driver_subgraphs.size(),
2333 [&]( const int a, const int b)
2334 {
2335 for( int ii = a; ii < b; ++ii )
2336 updateItemConnectionsTask( m_driver_subgraphs[ii] );
2337 });
2338 tp.wait_for_tasks();
2339
2342
2343 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2344 {
2345 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(),
2346 subgraph->m_driver_connection->NetCode() };
2347 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
2348
2349 m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
2350 }
2351
2352 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Prj().GetProjectFile().m_NetSettings;
2353 std::map<wxString, wxString> oldAssignments = netSettings->m_NetClassLabelAssignments;
2354
2355 netSettings->m_NetClassLabelAssignments.clear();
2356
2357 auto dirtySubgraphs =
2358 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2359 {
2360 if( aChangedItemHandler )
2361 {
2362 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2363 {
2364 for( SCH_ITEM* item : subgraph->m_items )
2365 (*aChangedItemHandler)( item );
2366 }
2367 }
2368 };
2369
2370 auto checkNetclassDrivers =
2371 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2372 {
2373 const CONNECTION_SUBGRAPH* driverSubgraph = nullptr;
2374 wxString netclass;
2375
2376 wxCHECK_RET( !subgraphs.empty(), wxS( "Invalid empty subgraph" ) );
2377
2378 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2379 {
2380 for( SCH_ITEM* item : subgraph->m_items )
2381 {
2382 const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProviders =
2383 subgraph->GetNetclassesForDriver( item, false );
2384
2385 if( netclassesWithProviders.size() > 0 )
2386 {
2387 netclass = netclassesWithProviders[0].first;
2388 break;
2389 }
2390 }
2391
2392 if( !netclass.IsEmpty() )
2393 {
2394 driverSubgraph = subgraph;
2395 break;
2396 }
2397 }
2398
2399 if( !driverSubgraph )
2400 driverSubgraph = subgraphs.front();
2401
2402 const wxString netname = driverSubgraph->GetNetName();
2403
2404 if( driverSubgraph->m_driver_connection->IsBus() )
2405 {
2406 for( const auto& member : driverSubgraph->m_driver_connection->Members() )
2407 {
2408 netSettings->m_NetClassLabelAssignments[ member->Name() ] = netclass;
2409
2410 auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
2411
2412 if( ii != m_net_name_to_subgraphs_map.end() )
2413 dirtySubgraphs( ii->second );
2414 }
2415 }
2416
2417 netSettings->m_NetClassLabelAssignments[ netname ] = netclass;
2418
2419 if( oldAssignments[ netname ] != netclass )
2420 dirtySubgraphs( subgraphs );
2421 };
2422
2423 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
2424 checkNetclassDrivers( subgraphs );
2425
2426 if( !aUnconditional )
2427 {
2428 for( auto& [ netname, netclass ] : oldAssignments )
2429 {
2430 if( netSettings->m_NetClassLabelAssignments.count( netname ) )
2431 continue;
2432
2433 netSettings->m_NetClassLabelAssignments[ netname ] = netclass;
2434 }
2435 }
2436}
2437
2438
2439int CONNECTION_GRAPH::getOrCreateNetCode( const wxString& aNetName )
2440{
2441 int code;
2442
2443 auto it = m_net_name_to_code_map.find( aNetName );
2444
2445 if( it == m_net_name_to_code_map.end() )
2446 {
2447 code = m_last_net_code++;
2448 m_net_name_to_code_map[ aNetName ] = code;
2449 }
2450 else
2451 {
2452 code = it->second;
2453 }
2454
2455 return code;
2456}
2457
2458
2460{
2461 int code = getOrCreateNetCode( aConnection.Name() );
2462
2463 aConnection.SetNetCode( code );
2464
2465 return code;
2466}
2467
2468
2470{
2471 std::vector<std::shared_ptr<SCH_CONNECTION>> connections_to_check( aConnection->Members() );
2472
2473 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2474 {
2475 const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
2476
2477 if( member->IsBus() )
2478 {
2479 connections_to_check.insert( connections_to_check.end(),
2480 member->Members().begin(),
2481 member->Members().end() );
2482 continue;
2483 }
2484
2485 assignNewNetCode( *member );
2486 }
2487}
2488
2489
2491{
2492 SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
2493 std::vector<CONNECTION_SUBGRAPH*> search_list;
2494 std::unordered_set<CONNECTION_SUBGRAPH*> visited;
2495 std::unordered_set<SCH_CONNECTION*> stale_bus_members;
2496
2497 auto visit =[&]( CONNECTION_SUBGRAPH* aParent )
2498 {
2499 for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
2500 {
2501 SCH_SHEET_PATH path = aParent->m_sheet;
2502 path.push_back( pin->GetParent() );
2503
2504 auto it = m_sheet_to_subgraphs_map.find( path );
2505
2506 if( it == m_sheet_to_subgraphs_map.end() )
2507 continue;
2508
2509 for( CONNECTION_SUBGRAPH* candidate : it->second )
2510 {
2511 if( !candidate->m_strong_driver
2512 || candidate->m_hier_ports.empty()
2513 || visited.contains( candidate ) )
2514 {
2515 continue;
2516 }
2517
2518 for( SCH_HIERLABEL* label : candidate->m_hier_ports )
2519 {
2520 if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
2521 {
2522 wxLogTrace( ConnTrace, wxS( "%lu: found child %lu (%s)" ), aParent->m_code,
2523 candidate->m_code, candidate->m_driver_connection->Name() );
2524
2525 candidate->m_hier_parent = aParent;
2526 aParent->m_hier_children.insert( candidate );
2527
2528 wxASSERT( candidate->m_graph == aParent->m_graph );
2529
2530 search_list.push_back( candidate );
2531 break;
2532 }
2533 }
2534 }
2535 }
2536
2537 for( SCH_HIERLABEL* label : aParent->m_hier_ports )
2538 {
2539 SCH_SHEET_PATH path = aParent->m_sheet;
2540 path.pop_back();
2541
2542 auto it = m_sheet_to_subgraphs_map.find( path );
2543
2544 if( it == m_sheet_to_subgraphs_map.end() )
2545 continue;
2546
2547 for( CONNECTION_SUBGRAPH* candidate : it->second )
2548 {
2549 if( candidate->m_hier_pins.empty()
2550 || visited.contains( candidate )
2551 || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
2552 {
2553 continue;
2554 }
2555
2556 const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
2557
2558 for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
2559 {
2560 // If the last sheet UUIDs won't match, no need to check the full path
2561 if( pin->GetParent()->m_Uuid != last_parent_uuid )
2562 continue;
2563
2564 SCH_SHEET_PATH pin_path = path;
2565 pin_path.push_back( pin->GetParent() );
2566
2567 if( pin_path != aParent->m_sheet )
2568 continue;
2569
2570 if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
2571 {
2572 wxLogTrace( ConnTrace, wxS( "%lu: found additional parent %lu (%s)" ),
2573 aParent->m_code, candidate->m_code,
2574 candidate->m_driver_connection->Name() );
2575
2576 aParent->m_hier_children.insert( candidate );
2577 search_list.push_back( candidate );
2578 break;
2579 }
2580 }
2581 }
2582 }
2583 };
2584
2585 auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph )
2586 {
2587 for( const auto& kv : aParentGraph->m_bus_neighbors )
2588 {
2589 for( CONNECTION_SUBGRAPH* neighbor : kv.second )
2590 {
2591 // May have been absorbed but won't have been deleted
2592 while( neighbor->m_absorbed )
2593 neighbor = neighbor->m_absorbed_by;
2594
2595 SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
2596
2597 // Now member may be out of date, since we just cloned the
2598 // connection from higher up in the hierarchy. We need to
2599 // figure out what the actual new connection is.
2600 SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
2601
2602 if( !member )
2603 {
2604 // Try harder: we might match on a secondary driver
2605 for( CONNECTION_SUBGRAPH* sg : kv.second )
2606 {
2607 if( sg->m_multiple_drivers )
2608 {
2609 SCH_SHEET_PATH sheet = sg->m_sheet;
2610
2611 for( SCH_ITEM* driver : sg->m_drivers )
2612 {
2613 auto c = getDefaultConnection( driver, sg );
2614 member = matchBusMember( parent, c.get() );
2615
2616 if( member )
2617 break;
2618 }
2619 }
2620
2621 if( member )
2622 break;
2623 }
2624 }
2625
2626 // This is bad, probably an ERC error
2627 if( !member )
2628 {
2629 wxLogTrace( ConnTrace, wxS( "Could not match bus member %s in %s" ),
2630 kv.first->Name(), parent->Name() );
2631 continue;
2632 }
2633
2634 auto neighbor_conn = neighbor->m_driver_connection;
2635 auto neighbor_name = neighbor_conn->Name();
2636
2637 // Matching name: no update needed
2638 if( neighbor_name == member->Name() )
2639 continue;
2640
2641 // Was this neighbor already updated from a different sheet? Don't rename it again
2642 if( neighbor_conn->Sheet() != neighbor->m_sheet )
2643 continue;
2644
2645 // Safety check against infinite recursion
2646 wxASSERT( neighbor_conn->IsNet() );
2647
2648 wxLogTrace( ConnTrace, wxS( "%lu (%s) connected to bus member %s (local %s)" ),
2649 neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
2650
2651 // Take whichever name is higher priority
2654 {
2655 member->Clone( *neighbor_conn );
2656 stale_bus_members.insert( member );
2657 }
2658 else
2659 {
2660 neighbor_conn->Clone( *member );
2661
2662 recacheSubgraphName( neighbor, neighbor_name );
2663
2664 // Recurse onto this neighbor in case it needs to re-propagate
2665 neighbor->m_dirty = true;
2666 propagateToNeighbors( neighbor, aForce );
2667 }
2668 }
2669 }
2670 };
2671
2672 // If we are a bus, we must propagate to local neighbors and then the hierarchy
2673 if( conn->IsBus() )
2674 propagate_bus_neighbors( aSubgraph );
2675
2676 // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
2677 // If we only have one or the other, process (we can either go bottom-up or top-down depending
2678 // on which subgraph comes up first)
2679 if( !aForce && !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
2680 {
2681 wxLogTrace( ConnTrace, wxS( "%lu (%s) has both hier ports and pins; deferring processing" ),
2682 aSubgraph->m_code, conn->Name() );
2683 return;
2684 }
2685 else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
2686 {
2687 wxLogTrace( ConnTrace,
2688 wxS( "%lu (%s) has no hier pins or ports on sheet %s; marking clean" ),
2689 aSubgraph->m_code, conn->Name(), aSubgraph->m_sheet.PathHumanReadable() );
2690 aSubgraph->m_dirty = false;
2691 return;
2692 }
2693
2694 visited.insert( aSubgraph );
2695
2696 wxLogTrace( ConnTrace, wxS( "Propagating %lu (%s) to subsheets" ),
2697 aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
2698
2699 visit( aSubgraph );
2700
2701 for( unsigned i = 0; i < search_list.size(); i++ )
2702 {
2703 auto child = search_list[i];
2704
2705 if( visited.insert( child ).second )
2706 visit( child );
2707
2708 child->m_dirty = false;
2709 }
2710
2711 // Now, find the best driver for this chain of subgraphs
2712 CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
2715 bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2716 wxString bestName = aSubgraph->m_driver_connection->Name();
2717
2718 // Check if a subsheet has a higher-priority connection to the same net
2720 {
2721 for( CONNECTION_SUBGRAPH* subgraph : visited )
2722 {
2723 if( subgraph == aSubgraph )
2724 continue;
2725
2727 CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
2728
2729 bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2730 wxString candidateName = subgraph->m_driver_connection->Name();
2731 bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
2732 bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
2733
2734 // Pick a better driving subgraph if it:
2735 // a) has a power pin or global driver
2736 // b) is a strong driver and we're a weak driver
2737 // c) is a higher priority strong driver
2738 // d) matches our priority, is a strong driver, and has a shorter path
2739 // e) matches our strength and is at least as short, and is alphabetically lower
2740
2741 if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
2742 ( !bestIsStrong && candidateStrong ) ||
2743 ( priority > highest && candidateStrong ) ||
2744 ( priority == highest && candidateStrong && shorterPath ) ||
2745 ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
2746 ( candidateName < bestName ) ) )
2747 {
2748 bestDriver = subgraph;
2749 highest = priority;
2750 bestIsStrong = candidateStrong;
2751 bestName = candidateName;
2752 }
2753 }
2754 }
2755
2756 if( bestDriver != aSubgraph )
2757 {
2758 wxLogTrace( ConnTrace, wxS( "%lu (%s) overridden by new driver %lu (%s)" ),
2759 aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
2760 bestDriver->m_driver_connection->Name() );
2761 }
2762
2763 conn = bestDriver->m_driver_connection;
2764
2765 for( CONNECTION_SUBGRAPH* subgraph : visited )
2766 {
2767 wxString old_name = subgraph->m_driver_connection->Name();
2768
2769 subgraph->m_driver_connection->Clone( *conn );
2770
2771 if( old_name != conn->Name() )
2772 recacheSubgraphName( subgraph, old_name );
2773
2774 if( conn->IsBus() )
2775 propagate_bus_neighbors( subgraph );
2776 }
2777
2778 // Somewhere along the way, a bus member may have been upgraded to a global or power label.
2779 // Because this can happen anywhere, we need a second pass to update all instances of that bus
2780 // member to have the correct connection info
2781 if( conn->IsBus() && !stale_bus_members.empty() )
2782 {
2783 std::unordered_set<SCH_CONNECTION*> cached_members = stale_bus_members;
2784
2785 for( SCH_CONNECTION* stale_member : cached_members )
2786 {
2787 for( CONNECTION_SUBGRAPH* subgraph : visited )
2788 {
2789 SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
2790 stale_member );
2791
2792 if( !member )
2793 {
2794 wxLogTrace( ConnTrace, wxS( "WARNING: failed to match stale member %s in %s." ),
2795 stale_member->Name(), subgraph->m_driver_connection->Name() );
2796 continue;
2797 }
2798
2799 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ), subgraph->m_code,
2800 subgraph->m_driver_connection->Name(), member->LocalName(),
2801 stale_member->Name() );
2802
2803 member->Clone( *stale_member );
2804
2805 propagate_bus_neighbors( subgraph );
2806 }
2807 }
2808 }
2809
2810 aSubgraph->m_dirty = false;
2811}
2812
2813
2814std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
2815 CONNECTION_SUBGRAPH* aSubgraph )
2816{
2817 std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
2818
2819 switch( aItem->Type() )
2820 {
2821 case SCH_PIN_T:
2822 {
2823 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
2824
2825 if( pin->IsGlobalPower() )
2826 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2827
2828 break;
2829 }
2830
2831 case SCH_GLOBAL_LABEL_T:
2832 case SCH_HIER_LABEL_T:
2833 case SCH_LABEL_T:
2834 {
2835 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2836 break;
2837 }
2838
2839 default:
2840 break;
2841 }
2842
2843 if( c )
2844 {
2845 c->SetGraph( this );
2846 c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
2847 }
2848
2849 return c;
2850}
2851
2852
2854 SCH_CONNECTION* aSearch )
2855{
2856 wxASSERT( aBusConnection->IsBus() );
2857
2858 SCH_CONNECTION* match = nullptr;
2859
2860 if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
2861 {
2862 // Vector bus: compare against index, because we allow the name
2863 // to be different
2864
2865 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
2866 {
2867 if( bus_member->VectorIndex() == aSearch->VectorIndex() )
2868 {
2869 match = bus_member.get();
2870 break;
2871 }
2872 }
2873 }
2874 else
2875 {
2876 // Group bus
2877 for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
2878 {
2879 // Vector inside group: compare names, because for bus groups
2880 // we expect the naming to be consistent across all usages
2881 // TODO(JE) explain this in the docs
2882 if( c->Type() == CONNECTION_TYPE::BUS )
2883 {
2884 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
2885 {
2886 if( bus_member->LocalName() == aSearch->LocalName() )
2887 {
2888 match = bus_member.get();
2889 break;
2890 }
2891 }
2892 }
2893 else if( c->LocalName() == aSearch->LocalName() )
2894 {
2895 match = c.get();
2896 break;
2897 }
2898 }
2899 }
2900
2901 return match;
2902}
2903
2904
2906 const wxString& aOldName )
2907{
2908 auto it = m_net_name_to_subgraphs_map.find( aOldName );
2909
2910 if( it != m_net_name_to_subgraphs_map.end() )
2911 {
2912 std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
2913 alg::delete_matching( vec, aSubgraph );
2914 }
2915
2916 wxLogTrace( ConnTrace, wxS( "recacheSubgraphName: %s => %s" ), aOldName,
2917 aSubgraph->m_driver_connection->Name() );
2918
2919 m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
2920}
2921
2922
2923std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
2924{
2925 auto it = m_bus_alias_cache.find( aName );
2926
2927 return it != m_bus_alias_cache.end() ? it->second : nullptr;
2928}
2929
2930
2931std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
2932{
2933 std::vector<const CONNECTION_SUBGRAPH*> ret;
2934
2935 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
2936 {
2937 // Graph is supposed to be up-to-date before calling this
2938 wxASSERT( !subgraph->m_dirty );
2939
2940 if( !subgraph->m_driver )
2941 continue;
2942
2943 SCH_SHEET_PATH* sheet = &subgraph->m_sheet;
2944 SCH_CONNECTION* connection = subgraph->m_driver->Connection( sheet );
2945
2946 if( !connection->IsBus() )
2947 continue;
2948
2949 auto labels = subgraph->GetVectorBusLabels();
2950
2951 if( labels.size() > 1 )
2952 {
2953 bool different = false;
2954 wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText( sheet, false );
2955
2956 for( unsigned i = 1; i < labels.size(); ++i )
2957 {
2958 if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText( sheet, false ) != first )
2959 {
2960 different = true;
2961 break;
2962 }
2963 }
2964
2965 if( !different )
2966 continue;
2967
2968 wxLogTrace( ConnTrace, wxS( "SG %ld (%s) has multiple bus labels" ), subgraph->m_code,
2969 connection->Name() );
2970
2971 ret.push_back( subgraph );
2972 }
2973 }
2974
2975 return ret;
2976}
2977
2978
2980{
2981 wxString retval = aSubGraph->GetNetName();
2982 bool found = false;
2983
2984 // This is a hacky way to find the true subgraph net name (why do we not store it?)
2985 // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
2986
2987 for( auto it = m_net_name_to_subgraphs_map.begin();
2988 it != m_net_name_to_subgraphs_map.end() && !found; ++it )
2989 {
2990 for( CONNECTION_SUBGRAPH* graph : it->second )
2991 {
2992 if( graph == aSubGraph )
2993 {
2994 retval = it->first;
2995 found = true;
2996 break;
2997 }
2998 }
2999 }
3000
3001 return retval;
3002}
3003
3004
3006 const SCH_SHEET_PATH& aPath )
3007{
3008 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3009
3010 if( it == m_net_name_to_subgraphs_map.end() )
3011 return nullptr;
3012
3013 for( CONNECTION_SUBGRAPH* sg : it->second )
3014 {
3015 // Cache is supposed to be valid by now
3016 wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
3017
3018 if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
3019 return sg;
3020 }
3021
3022 return nullptr;
3023}
3024
3025
3027{
3028 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3029
3030 if( it == m_net_name_to_subgraphs_map.end() )
3031 return nullptr;
3032
3033 wxASSERT( !it->second.empty() );
3034
3035 return it->second[0];
3036}
3037
3038
3040{
3041 auto it = m_item_to_subgraph_map.find( aItem );
3042 CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
3043
3044 while( ret && ret->m_absorbed )
3045 ret = ret->m_absorbed_by;
3046
3047 return ret;
3048}
3049
3050
3051const std::vector<CONNECTION_SUBGRAPH*>
3052CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
3053{
3054 std::vector<CONNECTION_SUBGRAPH*> subgraphs;
3055
3056 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3057
3058 if( it == m_net_name_to_subgraphs_map.end() )
3059 return subgraphs;
3060
3061 return it->second;
3062}
3063
3064
3066{
3067 int error_count = 0;
3068
3069 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::RunERC" ) );
3070
3071 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3072
3073 // We don't want to run many ERC checks more than once on a given screen even though it may
3074 // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
3075 std::set<SCH_ITEM*> seenDriverInstances;
3076
3077 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3078 {
3079 // There shouldn't be any null sub-graph pointers.
3080 wxCHECK2( subgraph, continue );
3081
3082 // Graph is supposed to be up-to-date before calling RunERC()
3083 wxASSERT( !subgraph->m_dirty );
3084
3085 if( subgraph->m_absorbed )
3086 continue;
3087
3088 if( seenDriverInstances.count( subgraph->m_driver ) )
3089 continue;
3090
3091 if( subgraph->m_driver )
3092 seenDriverInstances.insert( subgraph->m_driver );
3093
3104 if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
3105 {
3106 if( !ercCheckMultipleDrivers( subgraph ) )
3107 error_count++;
3108 }
3109
3110 subgraph->ResolveDrivers( false );
3111
3112 if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
3113 {
3114 if( !ercCheckBusToNetConflicts( subgraph ) )
3115 error_count++;
3116 }
3117
3118 if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
3119 {
3120 if( !ercCheckBusToBusEntryConflicts( subgraph ) )
3121 error_count++;
3122 }
3123
3124 if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
3125 {
3126 if( !ercCheckBusToBusConflicts( subgraph ) )
3127 error_count++;
3128 }
3129
3130 if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
3131 {
3132 if( !ercCheckFloatingWires( subgraph ) )
3133 error_count++;
3134 }
3135
3138 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3139 {
3140 if( !ercCheckNoConnects( subgraph ) )
3141 error_count++;
3142 }
3143
3145 || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
3146 {
3147 if( !ercCheckLabels( subgraph ) )
3148 error_count++;
3149 }
3150 }
3151
3152 // Hierarchical sheet checking is done at the schematic level
3154 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3155 {
3156 error_count += ercCheckHierSheets();
3157 }
3158
3159 if( settings.IsTestEnabled( ERCE_NETCLASS_CONFLICT ) )
3160 {
3161 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
3162 {
3163 if( !ercCheckNetclassConflicts( subgraphs ) )
3164 error_count++;
3165 }
3166 }
3167
3168 if( settings.IsTestEnabled( ERCE_SINGLE_GLOBAL_LABEL ) )
3169 {
3170 error_count += ercCheckSingleGlobalLabel();
3171 }
3172
3173 return error_count;
3174}
3175
3176
3178{
3179 wxCHECK( aSubgraph, false );
3180
3181 if( aSubgraph->m_multiple_drivers )
3182 {
3183 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3184 {
3185 if( driver == aSubgraph->m_driver )
3186 continue;
3187
3188 if( driver->Type() == SCH_GLOBAL_LABEL_T
3189 || driver->Type() == SCH_HIER_LABEL_T
3190 || driver->Type() == SCH_LABEL_T
3191 || ( driver->Type() == SCH_PIN_T
3192 && static_cast<SCH_PIN*>( driver )->IsGlobalPower() ) )
3193 {
3194 const wxString& primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
3195 const wxString& secondaryName = aSubgraph->GetNameForDriver( driver );
3196
3197 if( primaryName == secondaryName )
3198 continue;
3199
3200 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
3201 "items; %s will be used in the netlist" ),
3202 primaryName, secondaryName, primaryName );
3203
3204 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
3205 ercItem->SetItems( aSubgraph->m_driver, driver );
3206 ercItem->SetSheetSpecificPath( aSubgraph->GetSheet() );
3207 ercItem->SetItemsSheetPaths( aSubgraph->GetSheet(), aSubgraph->m_sheet );
3208 ercItem->SetErrorMessage( msg );
3209
3210 SCH_MARKER* marker = new SCH_MARKER( ercItem, driver->GetPosition() );
3211 aSubgraph->m_sheet.LastScreen()->Append( marker );
3212
3213 return false;
3214 }
3215 }
3216 }
3217
3218 return true;
3219}
3220
3221
3222bool CONNECTION_GRAPH::ercCheckNetclassConflicts( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
3223{
3224 wxString firstNetclass;
3225 SCH_ITEM* firstNetclassDriver = nullptr;
3226 const SCH_SHEET_PATH* firstNetclassDriverSheet = nullptr;
3227 bool conflictFound = false;
3228
3229 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
3230 {
3231 for( SCH_ITEM* item : subgraph->m_items )
3232 {
3233 const std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProvider =
3234 subgraph->GetNetclassesForDriver( item, true );
3235
3236 if( netclassesWithProvider.size() == 0 )
3237 continue;
3238
3239 auto checkNetclass = [&]( const std::pair<wxString, SCH_ITEM*>& netclass )
3240 {
3241 if( netclass.first != firstNetclass )
3242 {
3243 if( !firstNetclassDriver )
3244 {
3245 firstNetclass = netclass.first;
3246 firstNetclassDriver = netclass.second;
3247 firstNetclassDriverSheet = &subgraph->GetSheet();
3248 }
3249 else
3250 {
3251 conflictFound = true;
3252
3253 std::shared_ptr<ERC_ITEM> ercItem =
3255 ercItem->SetItems( firstNetclassDriver, netclass.second );
3256 ercItem->SetSheetSpecificPath( subgraph->GetSheet() );
3257 ercItem->SetItemsSheetPaths( *firstNetclassDriverSheet,
3258 subgraph->GetSheet() );
3259
3260 SCH_MARKER* marker =
3261 new SCH_MARKER( ercItem, netclass.second->GetPosition() );
3262 subgraph->m_sheet.LastScreen()->Append( marker );
3263 }
3264 }
3265 };
3266
3267 for( const std::pair<wxString, SCH_ITEM*>& netclass : netclassesWithProvider )
3268 checkNetclass( netclass );
3269 }
3270 }
3271
3272 return conflictFound;
3273}
3274
3275
3277{
3278 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3279 SCH_SCREEN* screen = sheet.LastScreen();
3280
3281 SCH_ITEM* net_item = nullptr;
3282 SCH_ITEM* bus_item = nullptr;
3283 SCH_CONNECTION conn( this );
3284
3285 for( SCH_ITEM* item : aSubgraph->m_items )
3286 {
3287 switch( item->Type() )
3288 {
3289 case SCH_LINE_T:
3290 {
3291 if( item->GetLayer() == LAYER_BUS )
3292 bus_item = ( !bus_item ) ? item : bus_item;
3293 else
3294 net_item = ( !net_item ) ? item : net_item;
3295
3296 break;
3297 }
3298
3299 case SCH_LABEL_T:
3300 case SCH_GLOBAL_LABEL_T:
3301 case SCH_SHEET_PIN_T:
3302 case SCH_HIER_LABEL_T:
3303 {
3304 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3305 conn.ConfigureFromLabel( EscapeString( text->GetShownText( &sheet, false ),
3306 CTX_NETNAME ) );
3307
3308 if( conn.IsBus() )
3309 bus_item = ( !bus_item ) ? item : bus_item;
3310 else
3311 net_item = ( !net_item ) ? item : net_item;
3312
3313 break;
3314 }
3315
3316 default:
3317 break;
3318 }
3319 }
3320
3321 if( net_item && bus_item )
3322 {
3323 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
3324 ercItem->SetSheetSpecificPath( sheet );
3325 ercItem->SetItems( net_item, bus_item );
3326
3327 SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
3328 screen->Append( marker );
3329
3330 return false;
3331 }
3332
3333 return true;
3334}
3335
3336
3338{
3339 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3340 SCH_SCREEN* screen = sheet.LastScreen();
3341
3342 SCH_ITEM* label = nullptr;
3343 SCH_ITEM* port = nullptr;
3344
3345 for( SCH_ITEM* item : aSubgraph->m_items )
3346 {
3347 switch( item->Type() )
3348 {
3349 case SCH_TEXT_T:
3350 case SCH_GLOBAL_LABEL_T:
3351 {
3352 if( !label && item->Connection( &sheet )->IsBus() )
3353 label = item;
3354 break;
3355 }
3356
3357 case SCH_SHEET_PIN_T:
3358 case SCH_HIER_LABEL_T:
3359 {
3360 if( !port && item->Connection( &sheet )->IsBus() )
3361 port = item;
3362 break;
3363 }
3364
3365 default:
3366 break;
3367 }
3368 }
3369
3370 if( label && port )
3371 {
3372 bool match = false;
3373
3374 for( const auto& member : label->Connection( &sheet )->Members() )
3375 {
3376 for( const auto& test : port->Connection( &sheet )->Members() )
3377 {
3378 if( test != member && member->Name() == test->Name() )
3379 {
3380 match = true;
3381 break;
3382 }
3383 }
3384
3385 if( match )
3386 break;
3387 }
3388
3389 if( !match )
3390 {
3391 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
3392 ercItem->SetSheetSpecificPath( sheet );
3393 ercItem->SetItems( label, port );
3394
3395 SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
3396 screen->Append( marker );
3397
3398 return false;
3399 }
3400 }
3401
3402 return true;
3403}
3404
3405
3407{
3408 bool conflict = false;
3409 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3410 SCH_SCREEN* screen = sheet.LastScreen();
3411
3412 SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
3413 SCH_ITEM* bus_wire = nullptr;
3414 wxString bus_name;
3415
3416 if( !aSubgraph->m_driver_connection )
3417 {
3418 // Incomplete bus entry. Let the unconnected tests handle it.
3419 return true;
3420 }
3421
3422 for( SCH_ITEM* item : aSubgraph->m_items )
3423 {
3424 switch( item->Type() )
3425 {
3427 {
3428 if( !bus_entry )
3429 bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3430 break;
3431 }
3432
3433 default:
3434 break;
3435 }
3436 }
3437
3438 if( bus_entry && bus_entry->m_connected_bus_item )
3439 {
3440 bus_wire = bus_entry->m_connected_bus_item;
3441
3442 wxASSERT( bus_wire->Type() == SCH_LINE_T );
3443
3444 // In some cases, the connection list (SCH_CONNECTION*) can be null.
3445 // Skip null connections.
3446 if( bus_entry->Connection( &sheet )
3447 && bus_wire->Type() == SCH_LINE_T
3448 && bus_wire->Connection( &sheet ) )
3449 {
3450 conflict = true; // Assume a conflict; we'll reset if we find it's OK
3451
3452 bus_name = bus_wire->Connection( &sheet )->Name();
3453
3454 std::set<wxString> test_names;
3455 test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
3456
3457 wxString baseName = sheet.PathHumanReadable();
3458
3459 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3460 test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
3461
3462 for( const auto& member : bus_wire->Connection( &sheet )->Members() )
3463 {
3464 if( member->Type() == CONNECTION_TYPE::BUS )
3465 {
3466 for( const auto& sub_member : member->Members() )
3467 {
3468 if( test_names.count( sub_member->FullLocalName() ) )
3469 conflict = false;
3470 }
3471 }
3472 else if( test_names.count( member->FullLocalName() ) )
3473 {
3474 conflict = false;
3475 }
3476 }
3477 }
3478 }
3479
3480 // Don't report warnings if this bus member has been overridden by a higher priority power pin
3481 // or global label
3482 if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
3484 {
3485 conflict = false;
3486 }
3487
3488 if( conflict )
3489 {
3490 wxString netName = aSubgraph->m_driver_connection->Name();
3491 wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
3492 " member of that bus" ),
3493 UnescapeString( netName ),
3494 UnescapeString( bus_name ) );
3495 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
3496 ercItem->SetSheetSpecificPath( sheet );
3497 ercItem->SetItems( bus_entry, bus_wire );
3498 ercItem->SetErrorMessage( msg );
3499
3500 SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
3501 screen->Append( marker );
3502
3503 return false;
3504 }
3505
3506 return true;
3507}
3508
3509
3511{
3512 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3513 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3514 SCH_SCREEN* screen = sheet.LastScreen();
3515 bool ok = true;
3516 SCH_PIN* pin = nullptr;
3517
3518 std::set<SCH_PIN*> unique_pins;
3519 std::set<SCH_LABEL_BASE*> unique_labels;
3520
3521 wxString netName = GetResolvedSubgraphName( aSubgraph );
3522
3523 auto process_subgraph = [&]( const CONNECTION_SUBGRAPH* aProcessGraph )
3524 {
3525 // Any subgraph that contains a no-connect should not
3526 // more than one pin (which would indicate it is connected
3527 for( SCH_ITEM* item : aProcessGraph->m_items )
3528 {
3529 switch( item->Type() )
3530 {
3531 case SCH_PIN_T:
3532 {
3533 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3534
3535 // Only link NC to pin on the current subgraph being checked
3536 if( aProcessGraph == aSubgraph )
3537 pin = test_pin;
3538
3539 if( std::none_of( unique_pins.begin(), unique_pins.end(),
3540 [test_pin]( SCH_PIN* aPin )
3541 {
3542 return test_pin->IsStacked( aPin );
3543 }
3544 ))
3545 {
3546 unique_pins.insert( test_pin );
3547 }
3548
3549 break;
3550 }
3551
3552 case SCH_LABEL_T:
3553 case SCH_GLOBAL_LABEL_T:
3554 case SCH_HIER_LABEL_T:
3555 unique_labels.insert( static_cast<SCH_LABEL_BASE*>( item ) );
3557 default:
3558 break;
3559 }
3560 }
3561 };
3562
3563 auto it = m_net_name_to_subgraphs_map.find( netName );
3564
3565 if( it != m_net_name_to_subgraphs_map.end() )
3566 {
3567 for( const CONNECTION_SUBGRAPH* subgraph : it->second )
3568 {
3569 process_subgraph( subgraph );
3570 }
3571 }
3572 else
3573 {
3574 process_subgraph( aSubgraph );
3575 }
3576
3577 if( aSubgraph->m_no_connect != nullptr )
3578 {
3579 // Special case: If the subgraph being checked consists of only a hier port/pin and
3580 // a no-connect, we don't issue a "no-connect connected" warning just because
3581 // connections exist on the sheet on the other side of the link.
3582 VECTOR2I noConnectPos = aSubgraph->m_no_connect->GetPosition();
3583
3584 for( SCH_SHEET_PIN* hierPin : aSubgraph->m_hier_pins )
3585 {
3586 if( hierPin->GetPosition() == noConnectPos )
3587 return true;
3588 }
3589
3590 for( SCH_HIERLABEL* hierLabel : aSubgraph->m_hier_ports )
3591 {
3592 if( hierLabel->GetPosition() == noConnectPos )
3593 return true;
3594 }
3595
3596 if( unique_pins.size() > 1 && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
3597 {
3598 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
3599 ercItem->SetSheetSpecificPath( sheet );
3600 ercItem->SetItemsSheetPaths( sheet );
3601
3602 VECTOR2I pos;
3603
3604 if( pin )
3605 {
3606 ercItem->SetItems( pin, aSubgraph->m_no_connect );
3607 pos = pin->GetPosition();
3608 }
3609 else
3610 {
3611 ercItem->SetItems( aSubgraph->m_no_connect );
3612 pos = aSubgraph->m_no_connect->GetPosition();
3613 }
3614
3615 SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
3616 screen->Append( marker );
3617
3618 ok = false;
3619 }
3620
3621 if( unique_pins.empty() && unique_labels.empty() &&
3623 {
3624 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
3625 ercItem->SetItems( aSubgraph->m_no_connect );
3626 ercItem->SetSheetSpecificPath( sheet );
3627 ercItem->SetItemsSheetPaths( sheet );
3628
3629 SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
3630 screen->Append( marker );
3631
3632 ok = false;
3633 }
3634 }
3635 else
3636 {
3637 bool has_other_connections = false;
3638 std::vector<SCH_PIN*> pins;
3639
3640 // Any subgraph that lacks a no-connect and contains a pin should also
3641 // contain at least one other potential driver
3642
3643 for( SCH_ITEM* item : aSubgraph->m_items )
3644 {
3645 switch( item->Type() )
3646 {
3647 case SCH_PIN_T:
3648 {
3649 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3650
3651 // Stacked pins do not count as other connections but non-stacked pins do
3652 if( !has_other_connections && !pins.empty() && !test_pin->GetParentSymbol()->IsPower() )
3653 {
3654 for( SCH_PIN* other_pin : pins )
3655 {
3656 if( !test_pin->IsStacked( other_pin ) )
3657 {
3658 has_other_connections = true;
3659 break;
3660 }
3661 }
3662 }
3663
3664 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
3665
3666 break;
3667 }
3668
3669 default:
3670 if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
3671 has_other_connections = true;
3672
3673 break;
3674 }
3675 }
3676
3677 // For many checks, we can just use the first pin
3678 pin = pins.empty() ? nullptr : pins[0];
3679
3680 // But if there is a power pin, it might be connected elsewhere
3681 for( SCH_PIN* test_pin : pins )
3682 {
3683 // Prefer the pin is part of a real component rather than some stray power symbol
3684 // Or else we may fail walking connected components to a power symbol pin since we reject
3685 // starting at a power symbol
3686 if( test_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
3687 && !test_pin->IsGlobalPower() )
3688 {
3689 pin = test_pin;
3690 break;
3691 }
3692 }
3693
3694 // Check if power input pins connect to anything else via net name,
3695 // but not for power symbols (with visible or legacy invisible pins).
3696 // We want to throw unconnected errors for power symbols even if they are connected to other
3697 // net items by name, because usually failing to connect them graphically is a mistake
3698 if( pin && !has_other_connections
3699 && !pin->IsGlobalPower()
3700 && !pin->GetLibPin()->GetParentSymbol()->IsPower() )
3701 {
3702 wxString name = pin->Connection( &sheet )->Name();
3703 wxString local_name = pin->Connection( &sheet )->Name( true );
3704
3705 if( m_global_label_cache.count( name )
3706 || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
3707 {
3708 has_other_connections = true;
3709 }
3710 }
3711
3712 // Only one pin, and it's not a no-connect pin
3713 if( pin && !has_other_connections
3714 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
3715 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
3716 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3717 {
3718 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3719 ercItem->SetSheetSpecificPath( sheet );
3720 ercItem->SetItemsSheetPaths( sheet );
3721 ercItem->SetItems( pin );
3722
3723 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
3724 screen->Append( marker );
3725
3726 ok = false;
3727 }
3728
3729 // If there are multiple pins in this SG, they might be indirectly connected (by netname)
3730 // rather than directly connected (by wires). We want to flag dangling pins even if they
3731 // join nets with another pin, as it's often a mistake
3732 if( pins.size() > 1 )
3733 {
3734 for( SCH_PIN* testPin : pins )
3735 {
3736 // We only apply this test to power symbols, because other symbols have
3737 // pins that are meant to be dangling, but the power symbols have pins
3738 // that are *not* meant to be dangling.
3739 if( testPin->GetLibPin()->GetParentSymbol()->IsPower()
3740 && testPin->ConnectedItems( sheet ).empty()
3741 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3742 {
3743 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3744 ercItem->SetSheetSpecificPath( sheet );
3745 ercItem->SetItemsSheetPaths( sheet );
3746 ercItem->SetItems( testPin );
3747
3748 SCH_MARKER* marker = new SCH_MARKER( ercItem, testPin->GetPosition() );
3749 screen->Append( marker );
3750
3751 ok = false;
3752 }
3753 }
3754 }
3755 }
3756
3757 return ok;
3758}
3759
3760
3762{
3763 if( aSubgraph->m_driver )
3764 return true;
3765
3766 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3767 std::vector<SCH_ITEM*> wires;
3768
3769 // We've gotten this far, so we know we have no valid driver. All we need to do is check
3770 // for a wire that we can place the error on.
3771 for( SCH_ITEM* item : aSubgraph->m_items )
3772 {
3773 if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
3774 wires.emplace_back( item );
3775 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
3776 wires.emplace_back( item );
3777 }
3778
3779 if( !wires.empty() )
3780 {
3781 SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
3782
3783 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
3784 ercItem->SetSheetSpecificPath( sheet );
3785 ercItem->SetItems( wires[0],
3786 wires.size() > 1 ? wires[1] : nullptr,
3787 wires.size() > 2 ? wires[2] : nullptr,
3788 wires.size() > 3 ? wires[3] : nullptr );
3789
3790 SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
3791 screen->Append( marker );
3792
3793 return false;
3794 }
3795
3796 return true;
3797}
3798
3799
3801{
3802 // Label connection rules:
3803 // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
3804 // Local labels are flagged if they don't connect to any pins and don't have a no-connect
3805 // Global labels are flagged if they appear only once, don't connect to any local labels,
3806 // and don't have a no-connect marker
3807
3808 if( !aSubgraph->m_driver_connection )
3809 return true;
3810
3811 // Buses are excluded from this test: many users create buses with only a single instance
3812 // and it's not really a problem as long as the nets in the bus pass ERC
3813 if( aSubgraph->m_driver_connection->IsBus() )
3814 return true;
3815
3816 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3817 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3818 bool ok = true;
3819 int pinCount = 0;
3820 bool has_nc = !!aSubgraph->m_no_connect;
3821
3822 std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
3823
3824
3825 auto hasPins =
3826 []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> int
3827 {
3828 return
3829 std::count_if( aLocSubgraph->m_items.begin(), aLocSubgraph->m_items.end(),
3830 []( const SCH_ITEM* item )
3831 {
3832 return item->Type() == SCH_PIN_T;
3833 } );
3834 };
3835
3836 auto reportError = [&]( SCH_TEXT* aText, int errCode )
3837 {
3838 if( settings.IsTestEnabled( errCode ) )
3839 {
3840 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
3841 ercItem->SetSheetSpecificPath( sheet );
3842 ercItem->SetItems( aText );
3843
3844 SCH_MARKER* marker = new SCH_MARKER( ercItem, aText->GetPosition() );
3845 aSubgraph->m_sheet.LastScreen()->Append( marker );
3846 }
3847 };
3848
3849 pinCount = hasPins( aSubgraph );
3850
3851 for( SCH_ITEM* item : aSubgraph->m_items )
3852 {
3853 switch( item->Type() )
3854 {
3855 case SCH_LABEL_T:
3856 case SCH_GLOBAL_LABEL_T:
3857 case SCH_HIER_LABEL_T:
3858 {
3859 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3860
3861 label_map[item->Type()].push_back( text );
3862
3863 // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
3864 // we want to error if an individual label in the subgraph is floating, even if it's
3865 // connected to other valid things by way of another label on the same sheet.
3866 if( text->IsDangling() )
3867 {
3868 reportError( text, ERCE_LABEL_NOT_CONNECTED );
3869 return false;
3870 }
3871
3872 break;
3873 }
3874
3875 default:
3876 break;
3877 }
3878 }
3879
3880 if( label_map.empty() )
3881 return true;
3882
3883 // No-connects on net neighbors will be noticed before, but to notice them on bus parents we
3884 // need to walk the graph
3885 for( auto pair : aSubgraph->m_bus_parents )
3886 {
3887 for( CONNECTION_SUBGRAPH* busParent : pair.second )
3888 {
3889 if( busParent->m_no_connect )
3890 {
3891 has_nc = true;
3892 break;
3893 }
3894
3895 CONNECTION_SUBGRAPH* hp = busParent->m_hier_parent;
3896
3897 while( hp )
3898 {
3899 if( hp->m_no_connect )
3900 {
3901 has_nc = true;
3902 break;
3903 }
3904
3905 hp = hp->m_hier_parent;
3906 }
3907 }
3908 }
3909
3910 wxString netName = GetResolvedSubgraphName( aSubgraph );
3911
3912 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" ) );
3913
3914 // Labels that have multiple pins connected are not dangling (may be used for naming segments)
3915 // so leave them without errors here
3916 if( pinCount > 1 )
3917 return true;
3918
3919 for( auto& [type, label_vec] : label_map )
3920 {
3921
3922 switch( type )
3923 {
3924 case SCH_GLOBAL_LABEL_T:
3925 if( !settings.IsTestEnabled( ERCE_GLOBLABEL ) )
3926 continue;
3927
3928 break;
3929 default:
3930 if( !settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
3931 continue;
3932
3933 break;
3934 }
3935
3936 for( SCH_TEXT* text : label_vec )
3937 {
3938 int allPins = pinCount;
3939
3940 auto it = m_net_name_to_subgraphs_map.find( netName );
3941
3942 if( it != m_net_name_to_subgraphs_map.end() )
3943 {
3944 for( const CONNECTION_SUBGRAPH* neighbor : it->second )
3945 {
3946 if( neighbor == aSubgraph )
3947 continue;
3948
3949 if( neighbor->m_no_connect )
3950 has_nc = true;
3951
3952 allPins += hasPins( neighbor );
3953 }
3954 }
3955
3956 if( allPins == 1 && !has_nc )
3957 {
3958 reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL
3960 ok = false;
3961 }
3962
3963 if( allPins == 0 )
3964 {
3965 reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL
3967 ok = false;
3968 }
3969 }
3970 }
3971
3972 return ok;
3973}
3974
3975
3977{
3978 int errors = 0;
3979
3980 std::map<wxString, std::tuple<int, const SCH_ITEM*, SCH_SHEET_PATH>> labelData;
3981
3982 for( const SCH_SHEET_PATH& sheet : m_sheetList )
3983 {
3984 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
3985 {
3986 SCH_TEXT* labelText = static_cast<SCH_TEXT*>( item );
3987 wxString resolvedLabelText =
3988 EscapeString( labelText->GetShownText( &sheet, false ), CTX_NETNAME );
3989
3990 if( labelData.find( resolvedLabelText ) == labelData.end() )
3991 {
3992 labelData[resolvedLabelText] = { 1, item, sheet };
3993 }
3994 else
3995 {
3996 std::get<0>( labelData[resolvedLabelText] ) += 1;
3997 std::get<1>( labelData[resolvedLabelText] ) = nullptr;
3998 std::get<2>( labelData[resolvedLabelText] ) = sheet;
3999 }
4000 }
4001 }
4002
4003 for( const auto& label : labelData )
4004 {
4005 if( std::get<0>( label.second ) == 1 )
4006 {
4007 const SCH_SHEET_PATH& sheet = std::get<2>( label.second );
4008 const SCH_ITEM* item = std::get<1>( label.second );
4009
4010 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SINGLE_GLOBAL_LABEL );
4011 ercItem->SetItems( std::get<1>( label.second ) );
4012 ercItem->SetSheetSpecificPath( sheet );
4013 ercItem->SetItemsSheetPaths( sheet );
4014
4015 SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
4016 sheet.LastScreen()->Append( marker );
4017
4018 errors++;
4019 }
4020 }
4021
4022 return errors;
4023}
4024
4025
4027{
4028 int errors = 0;
4029
4030 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4031
4032 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4033 {
4034 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
4035 {
4036 SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
4037 SCH_SHEET_PATH parentSheetPath = sheet;
4038
4039 parentSheetPath.push_back( parentSheet );
4040
4041 std::map<wxString, SCH_SHEET_PIN*> pins;
4042 std::map<wxString, SCH_HIERLABEL*> labels;
4043
4044 for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
4045 {
4046 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4047 pins[ pin->GetShownText( &parentSheetPath, false ) ] = pin;
4048
4049 if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
4050 {
4051 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4052 ercItem->SetItems( pin );
4053 ercItem->SetSheetSpecificPath( sheet );
4054 ercItem->SetItemsSheetPaths( sheet );
4055
4056 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
4057 sheet.LastScreen()->Append( marker );
4058
4059 errors++;
4060 }
4061 }
4062
4063 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4064 {
4065 std::set<wxString> matchedPins;
4066
4067 for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
4068 {
4069 if( subItem->Type() == SCH_HIER_LABEL_T )
4070 {
4071 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
4072 wxString labelText = label->GetShownText( &parentSheetPath, false );
4073
4074 if( !pins.count( labelText ) )
4075 labels[ labelText ] = label;
4076 else
4077 matchedPins.insert( labelText );
4078 }
4079 }
4080
4081 for( const wxString& matched : matchedPins )
4082 pins.erase( matched );
4083
4084 for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
4085 {
4086 wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
4087 "label inside the sheet" ),
4088 UnescapeString( unmatched.first ) );
4089
4090 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4091 ercItem->SetItems( unmatched.second );
4092 ercItem->SetErrorMessage( msg );
4093 ercItem->SetSheetSpecificPath( sheet );
4094 ercItem->SetItemsSheetPaths( sheet );
4095
4096 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
4097 sheet.LastScreen()->Append( marker );
4098
4099 errors++;
4100 }
4101
4102 for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
4103 {
4104 wxString msg = wxString::Format( _( "Hierarchical label %s has no matching "
4105 "sheet pin in the parent sheet" ),
4106 UnescapeString( unmatched.first ) );
4107
4108 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4109 ercItem->SetItems( unmatched.second );
4110 ercItem->SetErrorMessage( msg );
4111 ercItem->SetSheetSpecificPath( parentSheetPath );
4112 ercItem->SetItemsSheetPaths( parentSheetPath );
4113
4114 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
4115 parentSheet->GetScreen()->Append( marker );
4116
4117 errors++;
4118 }
4119 }
4120 }
4121 }
4122
4123 return errors;
4124}
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,...
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem)
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.
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:252
Container for ERC settings.
Definition: erc_settings.h:120
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:136
Definition: kiid.h:49
A small class to help profiling.
Definition: profile.h:49
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition: profile.h:105
std::shared_ptr< NET_SETTINGS > m_NetSettings
Net settings for this project (owned here)
Definition: project_file.h:173
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:166
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:136
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:100
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:294
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:174
void ClearConnectedItems(const SCH_SHEET_PATH &aPath)
Clear all connections to this item.
Definition: sch_item.cpp:260
const SYMBOL * GetParentSymbol() const
Definition: sch_item.cpp:155
virtual const wxString & GetCachedDriverName() const
Definition: sch_item.cpp:330
const std::unordered_set< SCH_RULE_AREA * > & GetRuleAreaCache() const
Gets the cache of rule areas enclosing this item.
Definition: sch_item.h:646
void AddConnectionTo(const SCH_SHEET_PATH &aPath, SCH_ITEM *aItem)
Add a connection link between this item and another.
Definition: sch_item.cpp:275
int GetUnit() const
Definition: sch_item.h:237
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:289
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction)
Definition: sch_item.h:576
void SetConnectionGraph(CONNECTION_GRAPH *aGraph)
Updates the connection graph for all connections in this item.
Definition: sch_item.cpp:227
virtual bool HasCachedDriverName() const
Definition: sch_item.h:546
SCH_CONNECTION * GetOrInitConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Definition: sch_item.cpp:315
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:210
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:40
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:372
wxString GetDefaultNetName(const SCH_SHEET_PATH &aPath, bool aForceNoConnect=false)
Definition: sch_pin.cpp:1616
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Definition: sch_screen.cpp:150
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:109
SCH_LINE * GetBus(const VECTOR2I &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T) const
Definition: sch_screen.h:435
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:108
bool IsInNetlist() const
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve a list of the SCH_PINs for the given sheet path.
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:836
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition: sch_symbol.h:216
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:466
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:398
#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:163
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.