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