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