KiCad PCB EDA Suite
Loading...
Searching...
No Matches
connection_graph.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2018 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Jon Evans <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <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 <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->IsBus() || 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 for( SCH_ITEM* candidate_driver : candidate->m_drivers )
2188 {
2189 if( candidate->GetNameForDriver( candidate_driver ) == secondary_name )
2190 {
2191 wxLogTrace( ConnTrace, wxS( "Global %lu (%s) promoted to %s" ),
2192 candidate->m_code, candidate->m_driver_connection->Name(),
2193 subgraph->m_driver_connection->Name() );
2194
2195 candidate->m_driver_connection->Clone( *subgraph->m_driver_connection );
2196
2197 candidate->m_dirty = false;
2198 propagateToNeighbors( candidate, false );
2199 }
2200 }
2201 }
2202 }
2203 }
2204
2205 // This call will handle descending the hierarchy and updating child subgraphs
2206 propagateToNeighbors( subgraph, false );
2207 }
2208
2209 // After processing and allowing some to be skipped if they have hierarchical
2210 // pins connecting both up and down the hierarchy, we check to see if any of them
2211 // have not been processed. This would indicate that they do not have off-sheet connections
2212 // but we still need to handle the subgraph
2213 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2214 {
2215 if( subgraph->m_dirty )
2216 propagateToNeighbors( subgraph, true );
2217 }
2218
2219 // Handle buses that have been linked together somewhere by member (net) connections.
2220 // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
2221
2222 // For net subgraphs that have more than one bus parent, we need to ensure that those
2223 // buses are linked together in the final netlist. The final name of each bus might not
2224 // match the local name that was used to establish the parent-child relationship, because
2225 // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
2226 // we need to identify the appropriate bus members to link together (and their final names),
2227 // and then update all instances of the old name in the hierarchy.
2228 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2229 {
2230 // All SGs should have been processed by propagateToNeighbors above
2231 wxASSERT_MSG( !subgraph->m_dirty,
2232 wxS( "Subgraph not processed by propagateToNeighbors!" ) );
2233
2234 if( subgraph->m_bus_parents.size() < 2 )
2235 continue;
2236
2237 SCH_CONNECTION* conn = subgraph->m_driver_connection;
2238
2239 wxLogTrace( ConnTrace, wxS( "%lu (%s) has multiple bus parents" ),
2240 subgraph->m_code, conn->Name() );
2241
2242 wxASSERT( conn->IsNet() );
2243
2244 for( const auto& ii : subgraph->m_bus_parents )
2245 {
2246 SCH_CONNECTION* link_member = ii.first.get();
2247
2248 for( CONNECTION_SUBGRAPH* parent : ii.second )
2249 {
2250 while( parent->m_absorbed )
2251 parent = parent->m_absorbed_by;
2252
2253 SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
2254
2255 if( !match )
2256 {
2257 wxLogTrace( ConnTrace, wxS( "Warning: could not match %s inside %lu (%s)" ),
2258 conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
2259 continue;
2260 }
2261
2262 if( conn->Name() != match->Name() )
2263 {
2264 wxString old_name = match->Name();
2265
2266 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ),
2267 parent->m_code, parent->m_driver_connection->Name(),
2268 old_name, conn->Name() );
2269
2270 match->Clone( *conn );
2271
2272 auto jj = m_net_name_to_subgraphs_map.find( old_name );
2273
2274 if( jj == m_net_name_to_subgraphs_map.end() )
2275 continue;
2276
2277 for( CONNECTION_SUBGRAPH* old_sg : jj->second )
2278 {
2279 while( old_sg->m_absorbed )
2280 old_sg = old_sg->m_absorbed_by;
2281
2282 old_sg->m_driver_connection->Clone( *conn );
2283 }
2284 }
2285 }
2286 }
2287 }
2288
2289 auto updateItemConnectionsTask =
2290 [&]( CONNECTION_SUBGRAPH* subgraph ) -> size_t
2291 {
2292 // Make sure weakly-driven single-pin nets get the unconnected_ prefix
2293 if( !subgraph->m_strong_driver && subgraph->m_drivers.size() == 1 &&
2294 subgraph->m_driver->Type() == SCH_PIN_T )
2295 {
2296 SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
2297 wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
2298
2299 subgraph->m_driver_connection->ConfigureFromLabel( name );
2300 }
2301
2302 subgraph->m_dirty = false;
2303 subgraph->UpdateItemConnections();
2304
2305 // No other processing to do on buses
2306 if( subgraph->m_driver_connection->IsBus() )
2307 return 0;
2308
2309 // As a visual aid, we can check sheet pins that are driven by themselves to see
2310 // if they should be promoted to buses
2311 if( subgraph->m_driver && subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
2312 {
2313 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
2314
2315 if( SCH_SHEET* sheet = pin->GetParent() )
2316 {
2317 wxString pinText = pin->GetShownText( false );
2318 SCH_SCREEN* screen = sheet->GetScreen();
2319
2320 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
2321 {
2322 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
2323
2324 if( label->GetShownText( &subgraph->m_sheet, false ) == pinText )
2325 {
2326 SCH_SHEET_PATH path = subgraph->m_sheet;
2327 path.push_back( sheet );
2328
2329 SCH_CONNECTION* parent_conn = label->Connection( &path );
2330
2331 if( parent_conn && parent_conn->IsBus() )
2332 subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
2333
2334 break;
2335 }
2336 }
2337
2338 if( subgraph->m_driver_connection->IsBus() )
2339 return 0;
2340 }
2341 }
2342
2343 return 1;
2344 };
2345
2346 tp.push_loop( m_driver_subgraphs.size(),
2347 [&]( const int a, const int b)
2348 {
2349 for( int ii = a; ii < b; ++ii )
2350 updateItemConnectionsTask( m_driver_subgraphs[ii] );
2351 });
2352 tp.wait_for_tasks();
2353
2356
2357 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2358 {
2359 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(),
2360 subgraph->m_driver_connection->NetCode() };
2361 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
2362
2363 m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
2364 }
2365
2366 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Prj().GetProjectFile().m_NetSettings;
2367 std::map<wxString, std::set<wxString>> oldAssignments =
2368 netSettings->GetNetclassLabelAssignments();
2369 std::set<wxString> affectedNetclassNetAssignments;
2370
2371 netSettings->ClearNetclassLabelAssignments();
2372
2373 auto dirtySubgraphs =
2374 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2375 {
2376 if( aChangedItemHandler )
2377 {
2378 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2379 {
2380 for( SCH_ITEM* item : subgraph->m_items )
2381 (*aChangedItemHandler)( item );
2382 }
2383 }
2384 };
2385
2386 auto checkNetclassDrivers =
2387 [&]( const wxString& netName, const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2388 {
2389 wxCHECK_RET( !subgraphs.empty(), wxS( "Invalid empty subgraph" ) );
2390
2391 std::set<wxString> netclasses;
2392
2393 // Collect all netclasses on all subgraphs for this net
2394 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2395 {
2396 for( SCH_ITEM* item : subgraph->m_items )
2397 {
2398 std::vector<std::pair<wxString, SCH_ITEM*>> netclassesWithProviders =
2399 subgraph->GetNetclassesForDriver( item );
2400
2401 for( std::pair<wxString, SCH_ITEM*>& ncPair : netclassesWithProviders )
2402 netclasses.insert( std::move( ncPair.first ) );
2403 }
2404 }
2405
2406 // Append the netclasses to any included bus members
2407 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2408 {
2409 if( subgraph->m_driver_connection->IsBus() )
2410 {
2411 auto processBusMember = [&, this]( const SCH_CONNECTION* member )
2412 {
2413 if( !netclasses.empty() )
2414 {
2415 netSettings->AppendNetclassLabelAssignment( member->Name(), netclasses );
2416 }
2417
2418 auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
2419
2420 if( oldAssignments.count( member->Name() ) )
2421 {
2422 if( oldAssignments[member->Name()] != netclasses )
2423 {
2424 affectedNetclassNetAssignments.insert( member->Name() );
2425
2426 if( ii != m_net_name_to_subgraphs_map.end() )
2427 dirtySubgraphs( ii->second );
2428 }
2429 }
2430 else if( !netclasses.empty() )
2431 {
2432 affectedNetclassNetAssignments.insert( member->Name() );
2433
2434 if( ii != m_net_name_to_subgraphs_map.end() )
2435 dirtySubgraphs( ii->second );
2436 }
2437 };
2438
2439 for( const std::shared_ptr<SCH_CONNECTION>& member :
2440 subgraph->m_driver_connection->Members() )
2441 {
2442 // Check if this member itself is a bus (which can be the case
2443 // for vector buses as members of a bus, see
2444 // https://gitlab.com/kicad/code/kicad/-/issues/16545
2445 if( member->IsBus() )
2446 {
2447 for( const std::shared_ptr<SCH_CONNECTION>& nestedMember :
2448 member->Members() )
2449 {
2450 processBusMember( nestedMember.get() );
2451 }
2452 }
2453 else
2454 {
2455 processBusMember( member.get() );
2456 }
2457 }
2458 }
2459 }
2460
2461 // Assign the netclasses to the root netname
2462 if( !netclasses.empty() )
2463 {
2464 netSettings->AppendNetclassLabelAssignment( netName, netclasses );
2465 }
2466
2467 if( oldAssignments.count( netName ) )
2468 {
2469 if( oldAssignments[netName] != netclasses )
2470 {
2471 affectedNetclassNetAssignments.insert( netName );
2472 dirtySubgraphs( subgraphs );
2473 }
2474 }
2475 else if( !netclasses.empty() )
2476 {
2477 affectedNetclassNetAssignments.insert( netName );
2478 dirtySubgraphs( subgraphs );
2479 }
2480 };
2481
2482 // Check for netclass assignments
2483 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
2484 checkNetclassDrivers( netname, subgraphs );
2485
2486 if( !aUnconditional )
2487 {
2488 for( auto& [netname, netclasses] : oldAssignments )
2489 {
2490 if( netSettings->GetNetclassLabelAssignments().count( netname )
2491 || affectedNetclassNetAssignments.count( netname ) )
2492 {
2493 continue;
2494 }
2495
2496 netSettings->SetNetclassLabelAssignment( netname, netclasses );
2497 }
2498 }
2499}
2500
2501
2502int CONNECTION_GRAPH::getOrCreateNetCode( const wxString& aNetName )
2503{
2504 int code;
2505
2506 auto it = m_net_name_to_code_map.find( aNetName );
2507
2508 if( it == m_net_name_to_code_map.end() )
2509 {
2510 code = m_last_net_code++;
2511 m_net_name_to_code_map[ aNetName ] = code;
2512 }
2513 else
2514 {
2515 code = it->second;
2516 }
2517
2518 return code;
2519}
2520
2521
2523{
2524 int code = getOrCreateNetCode( aConnection.Name() );
2525
2526 aConnection.SetNetCode( code );
2527
2528 return code;
2529}
2530
2531
2533{
2534 std::vector<std::shared_ptr<SCH_CONNECTION>> connections_to_check( aConnection->Members() );
2535
2536 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2537 {
2538 const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
2539
2540 if( member->IsBus() )
2541 {
2542 connections_to_check.insert( connections_to_check.end(),
2543 member->Members().begin(),
2544 member->Members().end() );
2545 continue;
2546 }
2547
2548 assignNewNetCode( *member );
2549 }
2550}
2551
2552
2554{
2555 SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
2556 std::vector<CONNECTION_SUBGRAPH*> search_list;
2557 std::unordered_set<CONNECTION_SUBGRAPH*> visited;
2558 std::unordered_set<SCH_CONNECTION*> stale_bus_members;
2559
2560 auto visit =[&]( CONNECTION_SUBGRAPH* aParent )
2561 {
2562 for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
2563 {
2564 SCH_SHEET_PATH path = aParent->m_sheet;
2565 path.push_back( pin->GetParent() );
2566
2567 auto it = m_sheet_to_subgraphs_map.find( path );
2568
2569 if( it == m_sheet_to_subgraphs_map.end() )
2570 continue;
2571
2572 for( CONNECTION_SUBGRAPH* candidate : it->second )
2573 {
2574 if( !candidate->m_strong_driver
2575 || candidate->m_hier_ports.empty()
2576 || visited.contains( candidate ) )
2577 {
2578 continue;
2579 }
2580
2581 for( SCH_HIERLABEL* label : candidate->m_hier_ports )
2582 {
2583 if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
2584 {
2585 wxLogTrace( ConnTrace, wxS( "%lu: found child %lu (%s)" ), aParent->m_code,
2586 candidate->m_code, candidate->m_driver_connection->Name() );
2587
2588 candidate->m_hier_parent = aParent;
2589 aParent->m_hier_children.insert( candidate );
2590
2591 wxASSERT( candidate->m_graph == aParent->m_graph );
2592
2593 search_list.push_back( candidate );
2594 break;
2595 }
2596 }
2597 }
2598 }
2599
2600 for( SCH_HIERLABEL* label : aParent->m_hier_ports )
2601 {
2602 SCH_SHEET_PATH path = aParent->m_sheet;
2603 path.pop_back();
2604
2605 auto it = m_sheet_to_subgraphs_map.find( path );
2606
2607 if( it == m_sheet_to_subgraphs_map.end() )
2608 continue;
2609
2610 for( CONNECTION_SUBGRAPH* candidate : it->second )
2611 {
2612 if( candidate->m_hier_pins.empty()
2613 || visited.contains( candidate )
2614 || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
2615 {
2616 continue;
2617 }
2618
2619 const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
2620
2621 for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
2622 {
2623 // If the last sheet UUIDs won't match, no need to check the full path
2624 if( pin->GetParent()->m_Uuid != last_parent_uuid )
2625 continue;
2626
2627 SCH_SHEET_PATH pin_path = path;
2628 pin_path.push_back( pin->GetParent() );
2629
2630 if( pin_path != aParent->m_sheet )
2631 continue;
2632
2633 if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
2634 {
2635 wxLogTrace( ConnTrace, wxS( "%lu: found additional parent %lu (%s)" ),
2636 aParent->m_code, candidate->m_code,
2637 candidate->m_driver_connection->Name() );
2638
2639 aParent->m_hier_children.insert( candidate );
2640 search_list.push_back( candidate );
2641 break;
2642 }
2643 }
2644 }
2645 }
2646 };
2647
2648 auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph )
2649 {
2650 for( const auto& kv : aParentGraph->m_bus_neighbors )
2651 {
2652 for( CONNECTION_SUBGRAPH* neighbor : kv.second )
2653 {
2654 // May have been absorbed but won't have been deleted
2655 while( neighbor->m_absorbed )
2656 neighbor = neighbor->m_absorbed_by;
2657
2658 SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
2659
2660 // Now member may be out of date, since we just cloned the
2661 // connection from higher up in the hierarchy. We need to
2662 // figure out what the actual new connection is.
2663 SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
2664
2665 if( !member )
2666 {
2667 // Try harder: we might match on a secondary driver
2668 for( CONNECTION_SUBGRAPH* sg : kv.second )
2669 {
2670 if( sg->m_multiple_drivers )
2671 {
2672 SCH_SHEET_PATH sheet = sg->m_sheet;
2673
2674 for( SCH_ITEM* driver : sg->m_drivers )
2675 {
2676 auto c = getDefaultConnection( driver, sg );
2677 member = matchBusMember( parent, c.get() );
2678
2679 if( member )
2680 break;
2681 }
2682 }
2683
2684 if( member )
2685 break;
2686 }
2687 }
2688
2689 // This is bad, probably an ERC error
2690 if( !member )
2691 {
2692 wxLogTrace( ConnTrace, wxS( "Could not match bus member %s in %s" ),
2693 kv.first->Name(), parent->Name() );
2694 continue;
2695 }
2696
2697 auto neighbor_conn = neighbor->m_driver_connection;
2698 auto neighbor_name = neighbor_conn->Name();
2699
2700 // Matching name: no update needed
2701 if( neighbor_name == member->Name() )
2702 continue;
2703
2704 // Was this neighbor already updated from a different sheet? Don't rename it again
2705 if( neighbor_conn->Sheet() != neighbor->m_sheet )
2706 continue;
2707
2708 // Safety check against infinite recursion
2709 wxASSERT( neighbor_conn->IsNet() );
2710
2711 wxLogTrace( ConnTrace, wxS( "%lu (%s) connected to bus member %s (local %s)" ),
2712 neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
2713
2714 // Take whichever name is higher priority
2717 {
2718 member->Clone( *neighbor_conn );
2719 stale_bus_members.insert( member );
2720 }
2721 else
2722 {
2723 neighbor_conn->Clone( *member );
2724
2725 recacheSubgraphName( neighbor, neighbor_name );
2726
2727 // Recurse onto this neighbor in case it needs to re-propagate
2728 neighbor->m_dirty = true;
2729 propagateToNeighbors( neighbor, aForce );
2730 }
2731 }
2732 }
2733 };
2734
2735 // If we are a bus, we must propagate to local neighbors and then the hierarchy
2736 if( conn->IsBus() )
2737 propagate_bus_neighbors( aSubgraph );
2738
2739 // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
2740 // If we only have one or the other, process (we can either go bottom-up or top-down depending
2741 // on which subgraph comes up first)
2742 if( !aForce && !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
2743 {
2744 wxLogTrace( ConnTrace, wxS( "%lu (%s) has both hier ports and pins; deferring processing" ),
2745 aSubgraph->m_code, conn->Name() );
2746 return;
2747 }
2748 else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
2749 {
2750 wxLogTrace( ConnTrace,
2751 wxS( "%lu (%s) has no hier pins or ports on sheet %s; marking clean" ),
2752 aSubgraph->m_code, conn->Name(), aSubgraph->m_sheet.PathHumanReadable() );
2753 aSubgraph->m_dirty = false;
2754 return;
2755 }
2756
2757 visited.insert( aSubgraph );
2758
2759 wxLogTrace( ConnTrace, wxS( "Propagating %lu (%s) to subsheets" ),
2760 aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
2761
2762 visit( aSubgraph );
2763
2764 for( unsigned i = 0; i < search_list.size(); i++ )
2765 {
2766 auto child = search_list[i];
2767
2768 if( visited.insert( child ).second )
2769 visit( child );
2770
2771 child->m_dirty = false;
2772 }
2773
2774 // Now, find the best driver for this chain of subgraphs
2775 CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
2778 bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2779 wxString bestName = aSubgraph->m_driver_connection->Name();
2780
2781 // Check if a subsheet has a higher-priority connection to the same net
2783 {
2784 for( CONNECTION_SUBGRAPH* subgraph : visited )
2785 {
2786 if( subgraph == aSubgraph )
2787 continue;
2788
2790 CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
2791
2792 bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2793 wxString candidateName = subgraph->m_driver_connection->Name();
2794 bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
2795 bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
2796
2797 // Pick a better driving subgraph if it:
2798 // a) has a power pin or global driver
2799 // b) is a strong driver and we're a weak driver
2800 // c) is a higher priority strong driver
2801 // d) matches our priority, is a strong driver, and has a shorter path
2802 // e) matches our strength and is at least as short, and is alphabetically lower
2803
2804 if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
2805 ( !bestIsStrong && candidateStrong ) ||
2806 ( priority > highest && candidateStrong ) ||
2807 ( priority == highest && candidateStrong && shorterPath ) ||
2808 ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
2809 ( candidateName < bestName ) ) )
2810 {
2811 bestDriver = subgraph;
2812 highest = priority;
2813 bestIsStrong = candidateStrong;
2814 bestName = candidateName;
2815 }
2816 }
2817 }
2818
2819 if( bestDriver != aSubgraph )
2820 {
2821 wxLogTrace( ConnTrace, wxS( "%lu (%s) overridden by new driver %lu (%s)" ),
2822 aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
2823 bestDriver->m_driver_connection->Name() );
2824 }
2825
2826 conn = bestDriver->m_driver_connection;
2827
2828 for( CONNECTION_SUBGRAPH* subgraph : visited )
2829 {
2830 wxString old_name = subgraph->m_driver_connection->Name();
2831
2832 subgraph->m_driver_connection->Clone( *conn );
2833
2834 if( old_name != conn->Name() )
2835 recacheSubgraphName( subgraph, old_name );
2836
2837 if( conn->IsBus() )
2838 propagate_bus_neighbors( subgraph );
2839 }
2840
2841 // Somewhere along the way, a bus member may have been upgraded to a global or power label.
2842 // Because this can happen anywhere, we need a second pass to update all instances of that bus
2843 // member to have the correct connection info
2844 if( conn->IsBus() && !stale_bus_members.empty() )
2845 {
2846 std::unordered_set<SCH_CONNECTION*> cached_members = stale_bus_members;
2847
2848 for( SCH_CONNECTION* stale_member : cached_members )
2849 {
2850 for( CONNECTION_SUBGRAPH* subgraph : visited )
2851 {
2852 SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
2853 stale_member );
2854
2855 if( !member )
2856 {
2857 wxLogTrace( ConnTrace, wxS( "WARNING: failed to match stale member %s in %s." ),
2858 stale_member->Name(), subgraph->m_driver_connection->Name() );
2859 continue;
2860 }
2861
2862 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ), subgraph->m_code,
2863 subgraph->m_driver_connection->Name(), member->LocalName(),
2864 stale_member->Name() );
2865
2866 member->Clone( *stale_member );
2867
2868 propagate_bus_neighbors( subgraph );
2869 }
2870 }
2871 }
2872
2873 aSubgraph->m_dirty = false;
2874}
2875
2876
2877std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
2878 CONNECTION_SUBGRAPH* aSubgraph )
2879{
2880 std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
2881
2882 switch( aItem->Type() )
2883 {
2884 case SCH_PIN_T:
2885 {
2886 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
2887
2888 if( pin->IsGlobalPower() )
2889 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2890
2891 break;
2892 }
2893
2894 case SCH_GLOBAL_LABEL_T:
2895 case SCH_HIER_LABEL_T:
2896 case SCH_LABEL_T:
2897 {
2898 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2899 break;
2900 }
2901
2902 default:
2903 break;
2904 }
2905
2906 if( c )
2907 {
2908 c->SetGraph( this );
2909 c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
2910 }
2911
2912 return c;
2913}
2914
2915
2917 SCH_CONNECTION* aSearch )
2918{
2919 wxASSERT( aBusConnection->IsBus() );
2920
2921 SCH_CONNECTION* match = nullptr;
2922
2923 if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
2924 {
2925 // Vector bus: compare against index, because we allow the name
2926 // to be different
2927
2928 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
2929 {
2930 if( bus_member->VectorIndex() == aSearch->VectorIndex() )
2931 {
2932 match = bus_member.get();
2933 break;
2934 }
2935 }
2936 }
2937 else
2938 {
2939 // Group bus
2940 for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
2941 {
2942 // Vector inside group: compare names, because for bus groups
2943 // we expect the naming to be consistent across all usages
2944 // TODO(JE) explain this in the docs
2945 if( c->Type() == CONNECTION_TYPE::BUS )
2946 {
2947 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
2948 {
2949 if( bus_member->LocalName() == aSearch->LocalName() )
2950 {
2951 match = bus_member.get();
2952 break;
2953 }
2954 }
2955 }
2956 else if( c->LocalName() == aSearch->LocalName() )
2957 {
2958 match = c.get();
2959 break;
2960 }
2961 }
2962 }
2963
2964 return match;
2965}
2966
2967
2969 const wxString& aOldName )
2970{
2971 auto it = m_net_name_to_subgraphs_map.find( aOldName );
2972
2973 if( it != m_net_name_to_subgraphs_map.end() )
2974 {
2975 std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
2976 alg::delete_matching( vec, aSubgraph );
2977 }
2978
2979 wxLogTrace( ConnTrace, wxS( "recacheSubgraphName: %s => %s" ), aOldName,
2980 aSubgraph->m_driver_connection->Name() );
2981
2982 m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
2983}
2984
2985
2986std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
2987{
2988 auto it = m_bus_alias_cache.find( aName );
2989
2990 return it != m_bus_alias_cache.end() ? it->second : nullptr;
2991}
2992
2993
2994std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
2995{
2996 std::vector<const CONNECTION_SUBGRAPH*> ret;
2997
2998 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
2999 {
3000 // Graph is supposed to be up-to-date before calling this
3001 wxASSERT( !subgraph->m_dirty );
3002
3003 if( !subgraph->m_driver )
3004 continue;
3005
3006 SCH_SHEET_PATH* sheet = &subgraph->m_sheet;
3007 SCH_CONNECTION* connection = subgraph->m_driver->Connection( sheet );
3008
3009 if( !connection->IsBus() )
3010 continue;
3011
3012 auto labels = subgraph->GetVectorBusLabels();
3013
3014 if( labels.size() > 1 )
3015 {
3016 bool different = false;
3017 wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText( sheet, false );
3018
3019 for( unsigned i = 1; i < labels.size(); ++i )
3020 {
3021 if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText( sheet, false ) != first )
3022 {
3023 different = true;
3024 break;
3025 }
3026 }
3027
3028 if( !different )
3029 continue;
3030
3031 wxLogTrace( ConnTrace, wxS( "SG %ld (%s) has multiple bus labels" ), subgraph->m_code,
3032 connection->Name() );
3033
3034 ret.push_back( subgraph );
3035 }
3036 }
3037
3038 return ret;
3039}
3040
3041
3043{
3044 wxString retval = aSubGraph->GetNetName();
3045 bool found = false;
3046
3047 // This is a hacky way to find the true subgraph net name (why do we not store it?)
3048 // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
3049
3050 for( auto it = m_net_name_to_subgraphs_map.begin();
3051 it != m_net_name_to_subgraphs_map.end() && !found; ++it )
3052 {
3053 for( CONNECTION_SUBGRAPH* graph : it->second )
3054 {
3055 if( graph == aSubGraph )
3056 {
3057 retval = it->first;
3058 found = true;
3059 break;
3060 }
3061 }
3062 }
3063
3064 return retval;
3065}
3066
3067
3069 const SCH_SHEET_PATH& aPath )
3070{
3071 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3072
3073 if( it == m_net_name_to_subgraphs_map.end() )
3074 return nullptr;
3075
3076 for( CONNECTION_SUBGRAPH* sg : it->second )
3077 {
3078 // Cache is supposed to be valid by now
3079 wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
3080
3081 if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
3082 return sg;
3083 }
3084
3085 return nullptr;
3086}
3087
3088
3090{
3091 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3092
3093 if( it == m_net_name_to_subgraphs_map.end() )
3094 return nullptr;
3095
3096 wxASSERT( !it->second.empty() );
3097
3098 return it->second[0];
3099}
3100
3101
3103{
3104 auto it = m_item_to_subgraph_map.find( aItem );
3105 CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
3106
3107 while( ret && ret->m_absorbed )
3108 ret = ret->m_absorbed_by;
3109
3110 return ret;
3111}
3112
3113
3114const std::vector<CONNECTION_SUBGRAPH*>&
3115CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
3116{
3117 static const std::vector<CONNECTION_SUBGRAPH*> subgraphs;
3118
3119 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3120
3121 if( it == m_net_name_to_subgraphs_map.end() )
3122 return subgraphs;
3123
3124 return it->second;
3125}
3126
3127
3129{
3130 int error_count = 0;
3131
3132 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::RunERC" ) );
3133
3134 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3135
3136 // We don't want to run many ERC checks more than once on a given screen even though it may
3137 // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
3138 std::set<SCH_ITEM*> seenDriverInstances;
3139
3140 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3141 {
3142 // There shouldn't be any null sub-graph pointers.
3143 wxCHECK2( subgraph, continue );
3144
3145 // Graph is supposed to be up-to-date before calling RunERC()
3146 wxASSERT( !subgraph->m_dirty );
3147
3148 if( subgraph->m_absorbed )
3149 continue;
3150
3151 if( seenDriverInstances.count( subgraph->m_driver ) )
3152 continue;
3153
3154 if( subgraph->m_driver )
3155 seenDriverInstances.insert( subgraph->m_driver );
3156
3167 if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
3168 {
3169 if( !ercCheckMultipleDrivers( subgraph ) )
3170 error_count++;
3171 }
3172
3173 subgraph->ResolveDrivers( false );
3174
3175 if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
3176 {
3177 if( !ercCheckBusToNetConflicts( subgraph ) )
3178 error_count++;
3179 }
3180
3181 if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
3182 {
3183 if( !ercCheckBusToBusEntryConflicts( subgraph ) )
3184 error_count++;
3185 }
3186
3187 if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
3188 {
3189 if( !ercCheckBusToBusConflicts( subgraph ) )
3190 error_count++;
3191 }
3192
3193 if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
3194 {
3195 if( !ercCheckFloatingWires( subgraph ) )
3196 error_count++;
3197 }
3198
3200 {
3201 if( !ercCheckDanglingWireEndpoints( subgraph ) )
3202 error_count++;
3203 }
3204
3207 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3208 {
3209 if( !ercCheckNoConnects( subgraph ) )
3210 error_count++;
3211 }
3212
3214 || settings.IsTestEnabled( ERCE_GLOBLABEL_DANGLING ) )
3215 {
3216 if( !ercCheckLabels( subgraph ) )
3217 error_count++;
3218 }
3219 }
3220
3221 if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
3222 {
3223 error_count += ercCheckDirectiveLabels();
3224 }
3225
3226 // Hierarchical sheet checking is done at the schematic level
3228 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3229 {
3230 error_count += ercCheckHierSheets();
3231 }
3232
3233 if( settings.IsTestEnabled( ERCE_SINGLE_GLOBAL_LABEL ) )
3234 {
3235 error_count += ercCheckSingleGlobalLabel();
3236 }
3237
3238 return error_count;
3239}
3240
3241
3243{
3244 wxCHECK( aSubgraph, false );
3245
3246 if( aSubgraph->m_multiple_drivers )
3247 {
3248 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3249 {
3250 if( driver == aSubgraph->m_driver )
3251 continue;
3252
3253 if( driver->Type() == SCH_GLOBAL_LABEL_T
3254 || driver->Type() == SCH_HIER_LABEL_T
3255 || driver->Type() == SCH_LABEL_T
3256 || ( driver->Type() == SCH_PIN_T
3257 && static_cast<SCH_PIN*>( driver )->IsGlobalPower() ) )
3258 {
3259 const wxString& primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
3260 const wxString& secondaryName = aSubgraph->GetNameForDriver( driver );
3261
3262 if( primaryName == secondaryName )
3263 continue;
3264
3265 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
3266 "items; %s will be used in the netlist" ),
3267 primaryName, secondaryName, primaryName );
3268
3269 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
3270 ercItem->SetItems( aSubgraph->m_driver, driver );
3271 ercItem->SetSheetSpecificPath( aSubgraph->GetSheet() );
3272 ercItem->SetItemsSheetPaths( aSubgraph->GetSheet(), aSubgraph->m_sheet );
3273 ercItem->SetErrorMessage( msg );
3274
3275 SCH_MARKER* marker = new SCH_MARKER( ercItem, driver->GetPosition() );
3276 aSubgraph->m_sheet.LastScreen()->Append( marker );
3277
3278 return false;
3279 }
3280 }
3281 }
3282
3283 return true;
3284}
3285
3286
3288{
3289 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3290 SCH_SCREEN* screen = sheet.LastScreen();
3291
3292 SCH_ITEM* net_item = nullptr;
3293 SCH_ITEM* bus_item = nullptr;
3294 SCH_CONNECTION conn( this );
3295
3296 for( SCH_ITEM* item : aSubgraph->m_items )
3297 {
3298 switch( item->Type() )
3299 {
3300 case SCH_LINE_T:
3301 {
3302 if( item->GetLayer() == LAYER_BUS )
3303 bus_item = ( !bus_item ) ? item : bus_item;
3304 else
3305 net_item = ( !net_item ) ? item : net_item;
3306
3307 break;
3308 }
3309
3310 case SCH_LABEL_T:
3311 case SCH_GLOBAL_LABEL_T:
3312 case SCH_SHEET_PIN_T:
3313 case SCH_HIER_LABEL_T:
3314 {
3315 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3316 conn.ConfigureFromLabel( EscapeString( text->GetShownText( &sheet, false ),
3317 CTX_NETNAME ) );
3318
3319 if( conn.IsBus() )
3320 bus_item = ( !bus_item ) ? item : bus_item;
3321 else
3322 net_item = ( !net_item ) ? item : net_item;
3323
3324 break;
3325 }
3326
3327 default:
3328 break;
3329 }
3330 }
3331
3332 if( net_item && bus_item )
3333 {
3334 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
3335 ercItem->SetSheetSpecificPath( sheet );
3336 ercItem->SetItems( net_item, bus_item );
3337
3338 SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
3339 screen->Append( marker );
3340
3341 return false;
3342 }
3343
3344 return true;
3345}
3346
3347
3349{
3350 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3351 SCH_SCREEN* screen = sheet.LastScreen();
3352
3353 SCH_ITEM* label = nullptr;
3354 SCH_ITEM* port = nullptr;
3355
3356 for( SCH_ITEM* item : aSubgraph->m_items )
3357 {
3358 switch( item->Type() )
3359 {
3360 case SCH_TEXT_T:
3361 case SCH_GLOBAL_LABEL_T:
3362 {
3363 if( !label && item->Connection( &sheet )->IsBus() )
3364 label = item;
3365 break;
3366 }
3367
3368 case SCH_SHEET_PIN_T:
3369 case SCH_HIER_LABEL_T:
3370 {
3371 if( !port && item->Connection( &sheet )->IsBus() )
3372 port = item;
3373 break;
3374 }
3375
3376 default:
3377 break;
3378 }
3379 }
3380
3381 if( label && port )
3382 {
3383 bool match = false;
3384
3385 for( const auto& member : label->Connection( &sheet )->Members() )
3386 {
3387 for( const auto& test : port->Connection( &sheet )->Members() )
3388 {
3389 if( test != member && member->Name() == test->Name() )
3390 {
3391 match = true;
3392 break;
3393 }
3394 }
3395
3396 if( match )
3397 break;
3398 }
3399
3400 if( !match )
3401 {
3402 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
3403 ercItem->SetSheetSpecificPath( sheet );
3404 ercItem->SetItems( label, port );
3405
3406 SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
3407 screen->Append( marker );
3408
3409 return false;
3410 }
3411 }
3412
3413 return true;
3414}
3415
3416
3418{
3419 bool conflict = false;
3420 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3421 SCH_SCREEN* screen = sheet.LastScreen();
3422
3423 SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
3424 SCH_ITEM* bus_wire = nullptr;
3425 wxString bus_name;
3426
3427 if( !aSubgraph->m_driver_connection )
3428 {
3429 // Incomplete bus entry. Let the unconnected tests handle it.
3430 return true;
3431 }
3432
3433 for( SCH_ITEM* item : aSubgraph->m_items )
3434 {
3435 switch( item->Type() )
3436 {
3438 {
3439 if( !bus_entry )
3440 bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3441 break;
3442 }
3443
3444 default:
3445 break;
3446 }
3447 }
3448
3449 if( bus_entry && bus_entry->m_connected_bus_item )
3450 {
3451 bus_wire = bus_entry->m_connected_bus_item;
3452
3453 wxASSERT( bus_wire->Type() == SCH_LINE_T );
3454
3455 // In some cases, the connection list (SCH_CONNECTION*) can be null.
3456 // Skip null connections.
3457 if( bus_entry->Connection( &sheet )
3458 && bus_wire->Type() == SCH_LINE_T
3459 && bus_wire->Connection( &sheet ) )
3460 {
3461 conflict = true; // Assume a conflict; we'll reset if we find it's OK
3462
3463 bus_name = bus_wire->Connection( &sheet )->Name();
3464
3465 std::set<wxString> test_names;
3466 test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
3467
3468 wxString baseName = sheet.PathHumanReadable();
3469
3470 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3471 test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
3472
3473 for( const auto& member : bus_wire->Connection( &sheet )->Members() )
3474 {
3475 if( member->Type() == CONNECTION_TYPE::BUS )
3476 {
3477 for( const auto& sub_member : member->Members() )
3478 {
3479 if( test_names.count( sub_member->FullLocalName() ) )
3480 conflict = false;
3481 }
3482 }
3483 else if( test_names.count( member->FullLocalName() ) )
3484 {
3485 conflict = false;
3486 }
3487 }
3488 }
3489 }
3490
3491 // Don't report warnings if this bus member has been overridden by a higher priority power pin
3492 // or global label
3493 if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
3495 {
3496 conflict = false;
3497 }
3498
3499 if( conflict )
3500 {
3501 wxString netName = aSubgraph->m_driver_connection->Name();
3502 wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
3503 " member of that bus" ),
3504 UnescapeString( netName ),
3505 UnescapeString( bus_name ) );
3506 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
3507 ercItem->SetSheetSpecificPath( sheet );
3508 ercItem->SetItems( bus_entry, bus_wire );
3509 ercItem->SetErrorMessage( msg );
3510
3511 SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
3512 screen->Append( marker );
3513
3514 return false;
3515 }
3516
3517 return true;
3518}
3519
3520
3522{
3523 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3524 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3525 SCH_SCREEN* screen = sheet.LastScreen();
3526 bool ok = true;
3527 SCH_PIN* pin = nullptr;
3528
3529 std::set<SCH_PIN*> unique_pins;
3530 std::set<SCH_LABEL_BASE*> unique_labels;
3531
3532 wxString netName = GetResolvedSubgraphName( aSubgraph );
3533
3534 auto process_subgraph = [&]( const CONNECTION_SUBGRAPH* aProcessGraph )
3535 {
3536 // Any subgraph that contains a no-connect should not
3537 // more than one pin (which would indicate it is connected
3538 for( SCH_ITEM* item : aProcessGraph->m_items )
3539 {
3540 switch( item->Type() )
3541 {
3542 case SCH_PIN_T:
3543 {
3544 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3545
3546 // Only link NC to pin on the current subgraph being checked
3547 if( aProcessGraph == aSubgraph )
3548 pin = test_pin;
3549
3550 if( std::none_of( unique_pins.begin(), unique_pins.end(),
3551 [test_pin]( SCH_PIN* aPin )
3552 {
3553 return test_pin->IsStacked( aPin );
3554 }
3555 ))
3556 {
3557 unique_pins.insert( test_pin );
3558 }
3559
3560 break;
3561 }
3562
3563 case SCH_LABEL_T:
3564 case SCH_GLOBAL_LABEL_T:
3565 case SCH_HIER_LABEL_T:
3566 unique_labels.insert( static_cast<SCH_LABEL_BASE*>( item ) );
3568 default:
3569 break;
3570 }
3571 }
3572 };
3573
3574 auto it = m_net_name_to_subgraphs_map.find( netName );
3575
3576 if( it != m_net_name_to_subgraphs_map.end() )
3577 {
3578 for( const CONNECTION_SUBGRAPH* subgraph : it->second )
3579 {
3580 process_subgraph( subgraph );
3581 }
3582 }
3583 else
3584 {
3585 process_subgraph( aSubgraph );
3586 }
3587
3588 if( aSubgraph->m_no_connect != nullptr )
3589 {
3590 // Special case: If the subgraph being checked consists of only a hier port/pin and
3591 // a no-connect, we don't issue a "no-connect connected" warning just because
3592 // connections exist on the sheet on the other side of the link.
3593 VECTOR2I noConnectPos = aSubgraph->m_no_connect->GetPosition();
3594
3595 for( SCH_SHEET_PIN* hierPin : aSubgraph->m_hier_pins )
3596 {
3597 if( hierPin->GetPosition() == noConnectPos )
3598 return true;
3599 }
3600
3601 for( SCH_HIERLABEL* hierLabel : aSubgraph->m_hier_ports )
3602 {
3603 if( hierLabel->GetPosition() == noConnectPos )
3604 return true;
3605 }
3606
3607 if( unique_pins.size() > 1 && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
3608 {
3609 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
3610 ercItem->SetSheetSpecificPath( sheet );
3611 ercItem->SetItemsSheetPaths( sheet );
3612
3613 VECTOR2I pos;
3614
3615 if( pin )
3616 {
3617 ercItem->SetItems( pin, aSubgraph->m_no_connect );
3618 pos = pin->GetPosition();
3619 }
3620 else
3621 {
3622 ercItem->SetItems( aSubgraph->m_no_connect );
3623 pos = aSubgraph->m_no_connect->GetPosition();
3624 }
3625
3626 SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
3627 screen->Append( marker );
3628
3629 ok = false;
3630 }
3631
3632 if( unique_pins.empty() && unique_labels.empty() &&
3634 {
3635 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
3636 ercItem->SetItems( aSubgraph->m_no_connect );
3637 ercItem->SetSheetSpecificPath( sheet );
3638 ercItem->SetItemsSheetPaths( sheet );
3639
3640 SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
3641 screen->Append( marker );
3642
3643 ok = false;
3644 }
3645 }
3646 else
3647 {
3648 bool has_other_connections = false;
3649 std::vector<SCH_PIN*> pins;
3650
3651 // Any subgraph that lacks a no-connect and contains a pin should also
3652 // contain at least one other potential driver
3653
3654 for( SCH_ITEM* item : aSubgraph->m_items )
3655 {
3656 switch( item->Type() )
3657 {
3658 case SCH_PIN_T:
3659 {
3660 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3661
3662 // Stacked pins do not count as other connections but non-stacked pins do
3663 if( !has_other_connections && !pins.empty() && !test_pin->GetParentSymbol()->IsPower() )
3664 {
3665 for( SCH_PIN* other_pin : pins )
3666 {
3667 if( !test_pin->IsStacked( other_pin ) )
3668 {
3669 has_other_connections = true;
3670 break;
3671 }
3672 }
3673 }
3674
3675 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
3676
3677 break;
3678 }
3679
3680 default:
3681 if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
3682 has_other_connections = true;
3683
3684 break;
3685 }
3686 }
3687
3688 // For many checks, we can just use the first pin
3689 pin = pins.empty() ? nullptr : pins[0];
3690
3691 // But if there is a power pin, it might be connected elsewhere
3692 for( SCH_PIN* test_pin : pins )
3693 {
3694 // Prefer the pin is part of a real component rather than some stray power symbol
3695 // Or else we may fail walking connected components to a power symbol pin since we reject
3696 // starting at a power symbol
3697 if( test_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
3698 && !test_pin->IsGlobalPower() )
3699 {
3700 pin = test_pin;
3701 break;
3702 }
3703 }
3704
3705 // Check if power input pins connect to anything else via net name,
3706 // but not for power symbols (with visible or legacy invisible pins).
3707 // We want to throw unconnected errors for power symbols even if they are connected to other
3708 // net items by name, because usually failing to connect them graphically is a mistake
3709 if( pin && !has_other_connections
3710 && !pin->IsGlobalPower()
3711 && !pin->GetLibPin()->GetParentSymbol()->IsPower() )
3712 {
3713 wxString name = pin->Connection( &sheet )->Name();
3714 wxString local_name = pin->Connection( &sheet )->Name( true );
3715
3716 if( m_global_label_cache.count( name )
3717 || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
3718 {
3719 has_other_connections = true;
3720 }
3721 }
3722
3723 // Only one pin, and it's not a no-connect pin
3724 if( pin && !has_other_connections
3725 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
3726 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
3727 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3728 {
3729 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3730 ercItem->SetSheetSpecificPath( sheet );
3731 ercItem->SetItemsSheetPaths( sheet );
3732 ercItem->SetItems( pin );
3733
3734 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
3735 screen->Append( marker );
3736
3737 ok = false;
3738 }
3739
3740 // If there are multiple pins in this SG, they might be indirectly connected (by netname)
3741 // rather than directly connected (by wires). We want to flag dangling pins even if they
3742 // join nets with another pin, as it's often a mistake
3743 if( pins.size() > 1 )
3744 {
3745 for( SCH_PIN* testPin : pins )
3746 {
3747 // We only apply this test to power symbols, because other symbols have
3748 // pins that are meant to be dangling, but the power symbols have pins
3749 // that are *not* meant to be dangling.
3750 if( testPin->GetLibPin()->GetParentSymbol()->IsPower()
3751 && testPin->ConnectedItems( sheet ).empty()
3752 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3753 {
3754 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3755 ercItem->SetSheetSpecificPath( sheet );
3756 ercItem->SetItemsSheetPaths( sheet );
3757 ercItem->SetItems( testPin );
3758
3759 SCH_MARKER* marker = new SCH_MARKER( ercItem, testPin->GetPosition() );
3760 screen->Append( marker );
3761
3762 ok = false;
3763 }
3764 }
3765 }
3766 }
3767
3768 return ok;
3769}
3770
3771
3773{
3774 int err_count = 0;
3775 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3776
3777 for( SCH_ITEM* item : aSubgraph->m_items )
3778 {
3779 if( item->GetLayer() != LAYER_WIRE )
3780 continue;
3781
3782 if( item->Type() == SCH_LINE_T )
3783 {
3784 SCH_LINE* line = static_cast<SCH_LINE*>( item );
3785
3786 if( line->IsGraphicLine() )
3787 continue;
3788
3789 auto report_error = [&]( VECTOR2I& location )
3790 {
3791 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNCONNECTED_WIRE_ENDPOINT );
3792
3793 ercItem->SetItems( line );
3794 ercItem->SetSheetSpecificPath( sheet );
3795 ercItem->SetErrorMessage( _( "Unconnected wire endpoint" ) );
3796
3797 SCH_MARKER* marker = new SCH_MARKER( ercItem, location );
3798 sheet.LastScreen()->Append( marker );
3799
3800 err_count++;
3801 };
3802
3803 if( line->IsStartDangling() )
3804 report_error( line->GetConnectionPoints()[0] );
3805
3806 if( line->IsEndDangling() )
3807 report_error( line->GetConnectionPoints()[1] );
3808 }
3809 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
3810 {
3811 SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3812
3813 auto report_error = [&]( VECTOR2I& location )
3814 {
3815 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNCONNECTED_WIRE_ENDPOINT );
3816
3817 ercItem->SetItems( entry );
3818 ercItem->SetSheetSpecificPath( sheet );
3819 ercItem->SetErrorMessage( _( "Unconnected wire to bus entry" ) );
3820
3821 SCH_MARKER* marker = new SCH_MARKER( ercItem, location );
3822 sheet.LastScreen()->Append( marker );
3823
3824 err_count++;
3825 };
3826
3827 if( entry->IsStartDangling() )
3828 report_error( entry->GetConnectionPoints()[0] );
3829
3830 if( entry->IsEndDangling() )
3831 report_error( entry->GetConnectionPoints()[1] );
3832 }
3833
3834 }
3835
3836 return err_count > 0;
3837}
3838
3839
3841{
3842 if( aSubgraph->m_driver )
3843 return true;
3844
3845 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3846 std::vector<SCH_ITEM*> wires;
3847
3848 // We've gotten this far, so we know we have no valid driver. All we need to do is check
3849 // for a wire that we can place the error on.
3850 for( SCH_ITEM* item : aSubgraph->m_items )
3851 {
3852 if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
3853 wires.emplace_back( item );
3854 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
3855 wires.emplace_back( item );
3856 }
3857
3858 if( !wires.empty() )
3859 {
3860 SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
3861
3862 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
3863 ercItem->SetSheetSpecificPath( sheet );
3864 ercItem->SetItems( wires[0],
3865 wires.size() > 1 ? wires[1] : nullptr,
3866 wires.size() > 2 ? wires[2] : nullptr,
3867 wires.size() > 3 ? wires[3] : nullptr );
3868
3869 SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
3870 screen->Append( marker );
3871
3872 return false;
3873 }
3874
3875 return true;
3876}
3877
3878
3880{
3881 // Label connection rules:
3882 // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
3883 // Local labels are flagged if they don't connect to any pins and don't have a no-connect
3884 // Global labels are flagged if they appear only once, don't connect to any local labels,
3885 // and don't have a no-connect marker
3886
3887 if( !aSubgraph->m_driver_connection )
3888 return true;
3889
3890 // Buses are excluded from this test: many users create buses with only a single instance
3891 // and it's not really a problem as long as the nets in the bus pass ERC
3892 if( aSubgraph->m_driver_connection->IsBus() )
3893 return true;
3894
3895 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3896 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3897 bool ok = true;
3898 size_t pinCount = 0;
3899 bool has_nc = !!aSubgraph->m_no_connect;
3900
3901 std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
3902
3903
3904 auto hasPins =
3905 []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> size_t
3906 {
3907 return std::count_if( aLocSubgraph->m_items.begin(), aLocSubgraph->m_items.end(),
3908 []( const SCH_ITEM* item )
3909 {
3910 return item->Type() == SCH_PIN_T;
3911 } );
3912 };
3913
3914 auto reportError =
3915 [&]( SCH_TEXT* aText, int errCode )
3916 {
3917 if( settings.IsTestEnabled( errCode ) )
3918 {
3919 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
3920 ercItem->SetSheetSpecificPath( sheet );
3921 ercItem->SetItems( aText );
3922
3923 SCH_MARKER* marker = new SCH_MARKER( ercItem, aText->GetPosition() );
3924 aSubgraph->m_sheet.LastScreen()->Append( marker );
3925 }
3926 };
3927
3928 pinCount = hasPins( aSubgraph );
3929
3930 for( SCH_ITEM* item : aSubgraph->m_items )
3931 {
3932 switch( item->Type() )
3933 {
3934 case SCH_LABEL_T:
3935 case SCH_GLOBAL_LABEL_T:
3936 case SCH_HIER_LABEL_T:
3937 {
3938 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3939
3940 label_map[item->Type()].push_back( text );
3941
3942 // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
3943 // we want to error if an individual label in the subgraph is floating, even if it's
3944 // connected to other valid things by way of another label on the same sheet.
3945 if( text->IsDangling() )
3946 {
3947 reportError( text, item->Type() == SCH_GLOBAL_LABEL_T ?
3950 return false;
3951 }
3952
3953 break;
3954 }
3955
3956 default:
3957 break;
3958 }
3959 }
3960
3961 if( label_map.empty() )
3962 return true;
3963
3964 // No-connects on net neighbors will be noticed before, but to notice them on bus parents we
3965 // need to walk the graph
3966 for( auto& [ connection, subgraphs ] : aSubgraph->m_bus_parents )
3967 {
3968 for( CONNECTION_SUBGRAPH* busParent : subgraphs )
3969 {
3970 if( busParent->m_no_connect )
3971 {
3972 has_nc = true;
3973 break;
3974 }
3975
3976 CONNECTION_SUBGRAPH* hp = busParent->m_hier_parent;
3977
3978 while( hp )
3979 {
3980 if( hp->m_no_connect )
3981 {
3982 has_nc = true;
3983 break;
3984 }
3985
3986 hp = hp->m_hier_parent;
3987 }
3988 }
3989 }
3990
3991 wxString netName = GetResolvedSubgraphName( aSubgraph );
3992
3993 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" ) );
3994
3995 // Labels that have multiple pins connected are not dangling (may be used for naming segments)
3996 // so leave them without errors here
3997 if( pinCount > 1 )
3998 return true;
3999
4000 for( auto& [type, label_vec] : label_map )
4001 {
4002 switch( type )
4003 {
4004 case SCH_GLOBAL_LABEL_T:
4005 if( !settings.IsTestEnabled( ERCE_GLOBLABEL_DANGLING ) )
4006 continue;
4007
4008 break;
4009 default:
4010 if( !settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
4011 continue;
4012
4013 break;
4014 }
4015
4016 for( SCH_TEXT* text : label_vec )
4017 {
4018 size_t allPins = pinCount;
4019
4020 auto it = m_net_name_to_subgraphs_map.find( netName );
4021
4022 if( it != m_net_name_to_subgraphs_map.end() )
4023 {
4024 for( const CONNECTION_SUBGRAPH* neighbor : it->second )
4025 {
4026 if( neighbor == aSubgraph )
4027 continue;
4028
4029 if( neighbor->m_no_connect )
4030 has_nc = true;
4031
4032 allPins += hasPins( neighbor );
4033 }
4034 }
4035
4036 if( allPins == 1 && !has_nc )
4037 {
4038 reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL_DANGLING
4040 ok = false;
4041 }
4042
4043 if( allPins == 0 )
4044 {
4045 reportError( text, type == SCH_GLOBAL_LABEL_T ? ERCE_GLOBLABEL_DANGLING
4047 ok = false;
4048 }
4049 }
4050 }
4051
4052 return ok;
4053}
4054
4055
4057{
4058 int errors = 0;
4059
4060 std::map<wxString, std::tuple<int, const SCH_ITEM*, SCH_SHEET_PATH>> labelData;
4061
4062 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4063 {
4064 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
4065 {
4066 SCH_TEXT* labelText = static_cast<SCH_TEXT*>( item );
4067 wxString resolvedLabelText =
4068 EscapeString( labelText->GetShownText( &sheet, false ), CTX_NETNAME );
4069
4070 if( labelData.find( resolvedLabelText ) == labelData.end() )
4071 {
4072 labelData[resolvedLabelText] = { 1, item, sheet };
4073 }
4074 else
4075 {
4076 std::get<0>( labelData[resolvedLabelText] ) += 1;
4077 std::get<1>( labelData[resolvedLabelText] ) = nullptr;
4078 std::get<2>( labelData[resolvedLabelText] ) = sheet;
4079 }
4080 }
4081 }
4082
4083 for( const auto& label : labelData )
4084 {
4085 if( std::get<0>( label.second ) == 1 )
4086 {
4087 const SCH_SHEET_PATH& sheet = std::get<2>( label.second );
4088 const SCH_ITEM* item = std::get<1>( label.second );
4089
4090 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SINGLE_GLOBAL_LABEL );
4091 ercItem->SetItems( std::get<1>( label.second ) );
4092 ercItem->SetSheetSpecificPath( sheet );
4093 ercItem->SetItemsSheetPaths( sheet );
4094
4095 SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
4096 sheet.LastScreen()->Append( marker );
4097
4098 errors++;
4099 }
4100 }
4101
4102 return errors;
4103}
4104
4105
4107{
4108 int error_count = 0;
4109
4110 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4111 {
4112 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_DIRECTIVE_LABEL_T ) )
4113 {
4114 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
4115
4116 if( label->IsDangling() )
4117 {
4118 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
4119 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
4120 ercItem->SetSheetSpecificPath( sheet );
4121 ercItem->SetItems( text );
4122
4123 SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
4124 sheet.LastScreen()->Append( marker );
4125 error_count++;
4126 }
4127 }
4128 }
4129
4130 return error_count;
4131}
4132
4133
4135{
4136 int errors = 0;
4137
4138 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4139
4140 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4141 {
4142 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
4143 {
4144 SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
4145 SCH_SHEET_PATH parentSheetPath = sheet;
4146
4147 parentSheetPath.push_back( parentSheet );
4148
4149 std::map<wxString, SCH_SHEET_PIN*> pins;
4150 std::map<wxString, SCH_HIERLABEL*> labels;
4151
4152 for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
4153 {
4154 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4155 pins[ pin->GetShownText( &parentSheetPath, false ) ] = pin;
4156
4157 if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
4158 {
4159 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4160 ercItem->SetItems( pin );
4161 ercItem->SetSheetSpecificPath( sheet );
4162 ercItem->SetItemsSheetPaths( sheet );
4163
4164 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
4165 sheet.LastScreen()->Append( marker );
4166
4167 errors++;
4168 }
4169 }
4170
4171 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4172 {
4173 std::set<wxString> matchedPins;
4174
4175 for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
4176 {
4177 if( subItem->Type() == SCH_HIER_LABEL_T )
4178 {
4179 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
4180 wxString labelText = label->GetShownText( &parentSheetPath, false );
4181
4182 if( !pins.count( labelText ) )
4183 labels[ labelText ] = label;
4184 else
4185 matchedPins.insert( labelText );
4186 }
4187 }
4188
4189 for( const wxString& matched : matchedPins )
4190 pins.erase( matched );
4191
4192 for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
4193 {
4194 wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
4195 "label inside the sheet" ),
4196 UnescapeString( unmatched.first ) );
4197
4198 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4199 ercItem->SetItems( unmatched.second );
4200 ercItem->SetErrorMessage( msg );
4201 ercItem->SetSheetSpecificPath( sheet );
4202 ercItem->SetItemsSheetPaths( sheet );
4203
4204 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
4205 sheet.LastScreen()->Append( marker );
4206
4207 errors++;
4208 }
4209
4210 for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
4211 {
4212 wxString msg = wxString::Format( _( "Hierarchical label %s has no matching "
4213 "sheet pin in the parent sheet" ),
4214 UnescapeString( unmatched.first ) );
4215
4216 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4217 ercItem->SetItems( unmatched.second );
4218 ercItem->SetErrorMessage( msg );
4219 ercItem->SetSheetSpecificPath( parentSheetPath );
4220 ercItem->SetItemsSheetPaths( parentSheetPath );
4221
4222 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
4223 parentSheet->GetScreen()->Append( marker );
4224
4225 errors++;
4226 }
4227 }
4228 }
4229 }
4230
4231 return errors;
4232}
const char * name
Definition: DXF_plotter.cpp:59
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.
const std::vector< CONNECTION_SUBGRAPH * > & GetAllSubgraphs(const wxString &aNetName) const
bool ercCheckMultipleDrivers(const CONNECTION_SUBGRAPH *aSubgraph)
If the subgraph has multiple drivers of equal priority that are graphically connected,...
SCH_SHEET_LIST m_sheetList
All the sheets in the schematic (as long as we don't have partial updates).
void generateGlobalPowerPinSubGraphs()
Iterate through the global power pins to collect the global labels as drivers.
std::unordered_map< wxString, int > m_net_name_to_code_map
int ercCheckSingleGlobalLabel()
Check that a global label is instantiated more that once across the schematic 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:244
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:183
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:200
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:161
SCH_SHEET & Root() const
Definition: schematic.h:130
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:97
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:319
Class for a bus to bus entry.
SCH_ITEM * m_connected_bus_items[2]
Pointer to the bus items (usually bus wires) connected to this bus-bus entry (either or both may be n...
bool IsStartDangling() const
Definition: sch_bus_entry.h:43
VECTOR2I GetPosition() const override
bool IsEndDangling() const
Definition: sch_bus_entry.h:44
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Class for a wire to bus entry.
SCH_ITEM * m_connected_bus_item
Pointer to the bus item (usually a bus wire) connected to this bus-wire entry, if it is connected to ...
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString FullLocalName() const
void ConfigureFromLabel(const wxString &aLabel)
Configures the connection given a label.
bool IsNet() const
void SetSubgraphCode(int aCode)
void SetBusCode(int aCode)
void SetName(const wxString &aName)
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:167
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:624
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:230
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:282
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction)
Definition: sch_item.h:554
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:539
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:819
bool IsDangling() const override
Definition: sch_label.h:335
LABEL_FLAG_SHAPE GetShape() const
Definition: sch_label.h:188
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:698
bool IsStartDangling() const
Definition: sch_line.h:266
bool IsEndDangling() const
Definition: sch_line.h:267
bool IsGraphicLine() const
Return if the line is a graphic (non electrical line)
Definition: sch_line.cpp:958
bool IsGlobalPower() const
Return whether this pin forms a global power connection: i.e., is part of a power symbol and of type ...
Definition: sch_pin.cpp:367
SCH_PIN * GetLibPin() const
Definition: sch_pin.h:81
bool IsStacked(const SCH_PIN *aPin) const
Definition: sch_pin.cpp:434
wxString GetDefaultNetName(const SCH_SHEET_PATH &aPath, bool aForceNoConnect=false)
Definition: sch_pin.cpp:1630
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:59
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:307
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:111
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:182
Schematic symbol object.
Definition: sch_symbol.h:77
bool IsInNetlist() const
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:832
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition: sch_symbol.h:185
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:181
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:440
@ LAYER_BUS
Definition: layer_ids.h:441
@ LAYER_JUNCTION
Definition: layer_ids.h:442
@ LAYER_BUS_JUNCTION
Definition: layer_ids.h:485
#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:156
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
Definition: string_utils.h:53
VECTOR2I location
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:30
static thread_pool * tp
Definition: thread_pool.cpp:28
BS::thread_pool thread_pool
Definition: thread_pool.h:31
#define kv
@ SCH_LINE_T
Definition: typeinfo.h: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.