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