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