KiCad PCB EDA Suite
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
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 wxString a_name = GetNameForDriver( a );
165 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
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 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
232{
233 if( !m_driver || m_dirty )
234 return "";
235
236 if( !m_driver->Connection( &m_sheet ) )
237 {
238#ifdef CONNECTIVITY_DEBUG
239 wxASSERT_MSG( false, "Tried to get the net name of an item with no connection" );
240#endif
241
242 return "";
243 }
244
245 return m_driver->Connection( &m_sheet )->Name();
246}
247
248
249std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetBusLabels() const
250{
251 std::vector<SCH_ITEM*> labels;
252
253 for( SCH_ITEM* item : m_drivers )
254 {
255 switch( item->Type() )
256 {
257 case SCH_LABEL_T:
259 {
260 SCH_CONNECTION* label_conn = item->Connection( &m_sheet );
261
262 // Only consider bus vectors
263 if( label_conn->Type() == CONNECTION_TYPE::BUS )
264 labels.push_back( item );
265
266 break;
267 }
268
269 default:
270 break;
271 }
272 }
273
274 return labels;
275}
276
277
279{
280 switch( aItem->Type() )
281 {
282 case SCH_PIN_T:
283 {
284 bool forceNoConnect = m_no_connect != nullptr;
285 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
286 return pin->GetDefaultNetName( m_sheet, forceNoConnect );
287 break;
288 }
289
290 case SCH_LABEL_T:
292 case SCH_HIER_LABEL_T:
293 case SCH_SHEET_PIN_T:
294 {
295 return EscapeString( static_cast<SCH_TEXT*>( aItem )->GetShownText(), CTX_NETNAME );
296 break;
297 }
298
299 default:
300 wxFAIL_MSG( "Unhandled item type in GetNameForDriver" );
301 break;
302 }
303
304 return wxEmptyString;
305}
306
307
309{
310 auto it = m_driver_name_cache.find( aItem );
311
312 if( it != m_driver_name_cache.end() )
313 return it->second;
314
315 m_driver_name_cache.emplace( aItem, driverName( aItem ) );
316
317 return m_driver_name_cache.at( aItem );
318}
319
320
322{
323 auto it = m_driver_name_cache.find( aItem );
324
325 if( it != m_driver_name_cache.end() )
326 return it->second;
327
328 return driverName( aItem );
329}
330
331
333{
334 wxString netclass;
335
336 aItem->RunOnChildren(
337 [&]( SCH_ITEM* aChild )
338 {
339 if( aChild->Type() == SCH_FIELD_T )
340 {
341 SCH_FIELD* field = static_cast<SCH_FIELD*>( aChild );
342
343 if( field->GetCanonicalName() == wxT( "Netclass" ) )
344 {
345 netclass = field->GetText();
346 return false;
347 }
348 }
349
350 return true;
351 } );
352
353 return netclass;
354}
355
356
358{
359 wxASSERT( m_sheet == aOther->m_sheet );
360
361 for( SCH_ITEM* item : aOther->m_items )
362 {
364 AddItem( item );
365 }
366
367 m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
368 m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
369
371
372 aOther->m_absorbed = true;
373 aOther->m_dirty = false;
374 aOther->m_driver = nullptr;
375 aOther->m_driver_connection = nullptr;
376 aOther->m_absorbed_by = this;
377}
378
379
381{
382 m_items.push_back( aItem );
383
384 if( aItem->Connection( &m_sheet )->IsDriver() )
385 m_drivers.push_back( aItem );
386
387 if( aItem->Type() == SCH_SHEET_PIN_T )
388 m_hier_pins.push_back( static_cast<SCH_SHEET_PIN*>( aItem ) );
389 else if( aItem->Type() == SCH_HIER_LABEL_T )
390 m_hier_ports.push_back( static_cast<SCH_HIERLABEL*>( aItem ) );
391}
392
393
395{
397 return;
398
399 for( SCH_ITEM* item : m_items )
400 {
401 SCH_CONNECTION* item_conn = item->GetOrInitConnection( m_sheet, m_graph );
402
403 if( !item_conn )
404 continue;
405
406 if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
407 ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
408 {
409 continue;
410 }
411
412 if( item != m_driver )
413 {
414 item_conn->Clone( *m_driver_connection );
415 item_conn->ClearDirty();
416 }
417 }
418}
419
420
422{
423 if( !aDriver )
424 return PRIORITY::NONE;
425
426 switch( aDriver->Type() )
427 {
432 case SCH_PIN_T:
433 {
434 SCH_PIN* sch_pin = static_cast<SCH_PIN*>( aDriver );
435
436 if( sch_pin->IsPowerConnection() )
437 return PRIORITY::POWER_PIN;
438 else
439 return PRIORITY::PIN;
440 }
441
442 default: return PRIORITY::NONE;
443 }
444}
445
446
448
449
451{
452 for( auto& subgraph : m_subgraphs )
453 delete subgraph;
454
455 m_items.clear();
456 m_subgraphs.clear();
457 m_driver_subgraphs.clear();
460 m_bus_alias_cache.clear();
466 m_local_label_cache.clear();
467 m_global_label_cache.clear();
468 m_last_net_code = 1;
469 m_last_bus_code = 1;
471}
472
473
474void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional,
475 std::function<void( SCH_ITEM* )>* aChangedItemHandler )
476{
477 PROF_TIMER recalc_time( "CONNECTION_GRAPH::Recalculate" );
478
479 if( aUnconditional )
480 Reset();
481
482 PROF_TIMER update_items( "updateItemConnectivity" );
483
484 m_sheetList = aSheetList;
485
486 for( const SCH_SHEET_PATH& sheet : aSheetList )
487 {
488 std::vector<SCH_ITEM*> items;
489 // Store current unit value, to regenerate it after calculations
490 // (useful in complex hierarchies)
491 std::vector<std::pair<SCH_SYMBOL*, int>> symbolsChanged;
492
493 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
494 {
495 if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
496 items.push_back( item );
497
498 // Ensure the hierarchy info stored in SCREENS is built and up to date
499 // (multi-unit symbols)
500 if( item->Type() == SCH_SYMBOL_T )
501 {
502 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
503 int new_unit = symbol->GetUnitSelection( &sheet );
504
505 // Store the initial unit value, to regenerate it after calculations,
506 // if modified
507 if( symbol->GetUnit() != new_unit )
508 symbolsChanged.push_back( { symbol, symbol->GetUnit() } );
509
510 symbol->UpdateUnit( new_unit );
511 }
512 }
513
514 m_items.reserve( m_items.size() + items.size() );
515
516 updateItemConnectivity( sheet, items );
517
518 // UpdateDanglingState() also adds connected items for SCH_TEXT
519 sheet.LastScreen()->TestDanglingEnds( &sheet, aChangedItemHandler );
520
521 // Restore the m_unit member, to avoid changes in current active sheet path
522 // after calculations
523 for( auto& item : symbolsChanged )
524 {
525 item.first->UpdateUnit( item.second );
526 }
527 }
528
529 if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
530 update_items.Show();
531
532 PROF_TIMER build_graph( "buildConnectionGraph" );
533
534 buildConnectionGraph( aChangedItemHandler );
535
536 if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
537 build_graph.Show();
538
539 recalc_time.Stop();
540
541 if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
542 recalc_time.Show();
543
544#ifndef DEBUG
545 // Pressure relief valve for release builds
546 const double max_recalc_time_msecs = 250.;
547
548 if( m_allowRealTime && ADVANCED_CFG::GetCfg().m_RealTimeConnectivity &&
549 recalc_time.msecs() > max_recalc_time_msecs )
550 {
551 m_allowRealTime = false;
552 }
553#endif
554}
555
556
558 const std::vector<SCH_ITEM*>& aItemList )
559{
560 std::map<VECTOR2I, std::vector<SCH_ITEM*>> connection_map;
561
562 for( SCH_ITEM* item : aItemList )
563 {
564 std::vector<VECTOR2I> points = item->GetConnectionPoints();
565 item->ConnectedItems( aSheet ).clear();
566
567 if( item->Type() == SCH_SHEET_T )
568 {
569 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
570 {
571 pin->InitializeConnection( aSheet, this );
572
573 pin->ConnectedItems( aSheet ).clear();
574
575 connection_map[ pin->GetTextPos() ].push_back( pin );
576 m_items.emplace_back( pin );
577 }
578 }
579 else if( item->Type() == SCH_SYMBOL_T )
580 {
581 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
582
583 for( SCH_PIN* pin : symbol->GetPins( &aSheet ) )
584 {
585 pin->InitializeConnection( aSheet, this );
586
587 VECTOR2I pos = pin->GetPosition();
588
589 // because calling the first time is not thread-safe
590 pin->GetDefaultNetName( aSheet );
591 pin->ConnectedItems( aSheet ).clear();
592
593 // Invisible power pins need to be post-processed later
594
595 if( pin->IsPowerConnection() && !pin->IsVisible() )
596 m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
597
598 connection_map[ pos ].push_back( pin );
599 m_items.emplace_back( pin );
600 }
601 }
602 else
603 {
604 m_items.emplace_back( item );
605 SCH_CONNECTION* conn = item->InitializeConnection( aSheet, this );
606
607 // Set bus/net property here so that the propagation code uses it
608 switch( item->Type() )
609 {
610 case SCH_LINE_T:
611 conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
613 break;
614
617 // clean previous (old) links:
618 static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
619 static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
620 break;
621
622 case SCH_PIN_T:
624 break;
625
628 // clean previous (old) link:
629 static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
630 break;
631
632 default:
633 break;
634 }
635
636 for( const VECTOR2I& point : points )
637 connection_map[ point ].push_back( item );
638 }
639
640 item->SetConnectivityDirty( false );
641 }
642
643 for( const auto& it : connection_map )
644 {
645 std::vector<SCH_ITEM*> connection_vec = it.second;
646 std::sort( connection_vec.begin(), connection_vec.end() );
647 connection_vec.erase( std::unique( connection_vec.begin(), connection_vec.end() ),
648 connection_vec.end() );
649
650 // Pre-scan to see if we have a bus at this location
651 SCH_LINE* busLine = aSheet.LastScreen()->GetBus( it.first );
652
653 std::mutex update_mutex;
654
655 auto update_lambda = [&]( SCH_ITEM* connected_item ) -> size_t
656 {
657 // Bus entries are special: they can have connection points in the
658 // middle of a wire segment, because the junction algo doesn't split
659 // the segment in two where you place a bus entry. This means that
660 // bus entries that don't land on the end of a line segment need to
661 // have "virtual" connection points to the segments they graphically
662 // touch.
663 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
664 {
665 // If this location only has the connection point of the bus
666 // entry itself, this means that either the bus entry is not
667 // connected to anything graphically, or that it is connected to
668 // a segment at some point other than at one of the endpoints.
669 if( connection_vec.size() == 1 )
670 {
671 if( busLine )
672 {
673 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
674 bus_entry->m_connected_bus_item = busLine;
675 }
676 }
677 }
678
679 // Bus-to-bus entries are treated just like bus wires
680 else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
681 {
682 if( connection_vec.size() < 2 )
683 {
684 if( busLine )
685 {
686 auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
687
688 if( it.first == bus_entry->GetPosition() )
689 bus_entry->m_connected_bus_items[0] = busLine;
690 else
691 bus_entry->m_connected_bus_items[1] = busLine;
692
693 std::lock_guard<std::mutex> lock( update_mutex );
694 bus_entry->AddConnectionTo( aSheet, busLine );
695 busLine->AddConnectionTo( aSheet, bus_entry );
696 }
697 }
698 }
699
700 // Change junctions to be on bus junction layer if they are touching a bus
701 else if( connected_item->Type() == SCH_JUNCTION_T )
702 {
703 connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
704 }
705
706 SCH_ITEM_SET& connected_set = connected_item->ConnectedItems( aSheet );
707 connected_set.reserve( connection_vec.size() );
708
709 for( SCH_ITEM* test_item : connection_vec )
710 {
711 bool bus_connection_ok = true;
712
713 if( test_item == connected_item )
714 continue;
715
716 // Set up the link between the bus entry net and the bus
717 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
718 {
719 if( test_item->GetLayer() == LAYER_BUS )
720 {
721 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
722 bus_entry->m_connected_bus_item = test_item;
723 }
724 }
725
726 // Bus entries only connect to bus lines on the end that is touching a bus line.
727 // If the user has overlapped another net line with the endpoint of the bus entry
728 // where the entry connects to a bus, we don't want to short-circuit it.
729 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
730 {
731 bus_connection_ok = !busLine || test_item->GetLayer() == LAYER_BUS;
732 }
733 else if( test_item->Type() == SCH_BUS_WIRE_ENTRY_T )
734 {
735 bus_connection_ok = !busLine || connected_item->GetLayer() == LAYER_BUS;
736 }
737
738 if( connected_item->ConnectionPropagatesTo( test_item ) &&
739 test_item->ConnectionPropagatesTo( connected_item ) &&
740 bus_connection_ok )
741 {
742 connected_set.push_back( test_item );
743 }
744 }
745
746 // If we got this far and did not find a connected bus item for a bus entry,
747 // we should do a manual scan in case there is a bus item on this connection
748 // point but we didn't pick it up earlier because there is *also* a net item here.
749 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
750 {
751 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
752
753 if( !bus_entry->m_connected_bus_item )
754 {
755 SCH_SCREEN* screen = aSheet.LastScreen();
756 SCH_LINE* bus = screen->GetBus( it.first );
757
758 if( bus )
759 bus_entry->m_connected_bus_item = bus;
760 }
761 }
762
763 return 1;
764 };
765
767
768 tp.push_loop( connection_vec.size(),
769 [&]( const int a, const int b)
770 {
771 for( int ii = a; ii < b; ++ii )
772 update_lambda( connection_vec[ii] );
773 });
774 tp.wait_for_tasks();
775 }
776}
777
778
780{
781 // Recache all bus aliases for later use
782 wxCHECK_RET( m_schematic, "Connection graph cannot be built without schematic pointer" );
783
784 SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
785
786 for( unsigned i = 0; i < all_sheets.size(); i++ )
787 {
788 for( const std::shared_ptr<BUS_ALIAS>& alias : all_sheets[i].LastScreen()->GetBusAliases() )
789 m_bus_alias_cache[ alias->GetName() ] = alias;
790 }
791
792 // Build subgraphs from items (on a per-sheet basis)
793
794 for( SCH_ITEM* item : m_items )
795 {
796 for( const auto& it : item->m_connection_map )
797 {
798 const SCH_SHEET_PATH& sheet = it.first;
799 SCH_CONNECTION* connection = it.second;
800
801 if( connection->SubgraphCode() == 0 )
802 {
803 CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
804
805 subgraph->m_code = m_last_subgraph_code++;
806 subgraph->m_sheet = sheet;
807
808 subgraph->AddItem( item );
809
810 connection->SetSubgraphCode( subgraph->m_code );
811 m_item_to_subgraph_map[item] = subgraph;
812
813 std::list<SCH_ITEM*> memberlist;
814
815 auto get_items =
816 [&]( SCH_ITEM* aItem ) -> bool
817 {
818 SCH_CONNECTION* conn = aItem->GetOrInitConnection( sheet, this );
819 bool unique = !( aItem->GetFlags() & CANDIDATE );
820
821 if( conn && !conn->SubgraphCode() )
822 aItem->SetFlags( CANDIDATE );
823
824 return ( unique && conn && ( conn->SubgraphCode() == 0 ) );
825 };
826
827 std::copy_if( item->ConnectedItems( sheet ).begin(),
828 item->ConnectedItems( sheet ).end(),
829 std::back_inserter( memberlist ), get_items );
830
831 for( SCH_ITEM* connected_item : memberlist )
832 {
833 if( connected_item->Type() == SCH_NO_CONNECT_T )
834 subgraph->m_no_connect = connected_item;
835
836 SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
837
838 wxASSERT( connected_conn );
839
840 if( connected_conn->SubgraphCode() == 0 )
841 {
842 connected_conn->SetSubgraphCode( subgraph->m_code );
843 m_item_to_subgraph_map[connected_item] = subgraph;
844 subgraph->AddItem( connected_item );
845 SCH_ITEM_SET& citemset = connected_item->ConnectedItems( sheet );
846
847 for( SCH_ITEM* citem : citemset )
848 {
849 if( citem->HasFlag( CANDIDATE ) )
850 continue;
851
852 if( get_items( citem ) )
853 memberlist.push_back( citem );
854 }
855 }
856 }
857
858 for( SCH_ITEM* connected_item : memberlist )
859 connected_item->ClearFlags( CANDIDATE );
860
861 subgraph->m_dirty = true;
862 m_subgraphs.push_back( subgraph );
863 }
864 }
865 }
866
867}
868
870{
871 // Resolve drivers for subgraphs and propagate connectivity info
872 std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
873
874 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
875 [&] ( const CONNECTION_SUBGRAPH* candidate )
876 {
877 return candidate->m_dirty;
878 } );
879
880 std::vector<std::future<size_t>> returns( dirty_graphs.size() );
881
882 auto update_lambda = []( CONNECTION_SUBGRAPH* subgraph ) -> size_t
883 {
884 if( !subgraph->m_dirty )
885 return 0;
886
887 // Special processing for some items
888 for( SCH_ITEM* item : subgraph->m_items )
889 {
890 switch( item->Type() )
891 {
892 case SCH_NO_CONNECT_T:
893 subgraph->m_no_connect = item;
894 break;
895
897 subgraph->m_bus_entry = item;
898 break;
899
900 case SCH_PIN_T:
901 {
902 auto pin = static_cast<SCH_PIN*>( item );
903
904 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
905 subgraph->m_no_connect = item;
906
907 break;
908 }
909
910 default:
911 break;
912 }
913 }
914
915 subgraph->ResolveDrivers( true );
916 subgraph->m_dirty = false;
917
918 return 1;
919 };
920
922
923 tp.push_loop( dirty_graphs.size(),
924 [&]( const int a, const int b)
925 {
926 for( int ii = a; ii < b; ++ii )
927 update_lambda( dirty_graphs[ii] );
928 });
929 tp.wait_for_tasks();
930
931 // Now discard any non-driven subgraphs from further consideration
932
933 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
934 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
935 {
936 return candidate->m_driver;
937 } );
938}
939
940
942{
943 // Check for subgraphs with the same net name but only weak drivers.
944 // For example, two wires that are both connected to hierarchical
945 // sheet pins that happen to have the same name, but are not the same.
946
947 for( auto&& subgraph : m_driver_subgraphs )
948 {
949 wxString full_name = subgraph->m_driver_connection->Name();
950 wxString name = subgraph->m_driver_connection->Name( true );
951 m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
952
953 // For vector buses, we need to cache the prefix also, as two different instances of the
954 // weakly driven pin may have the same prefix but different vector start and end. We need
955 // to treat those as needing renaming also, because otherwise if they end up on a sheet with
956 // common usage, they will be incorrectly merged.
957 if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
958 {
959 wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
960 m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
961 }
962
963 subgraph->m_dirty = true;
964
965 if( subgraph->m_strong_driver )
966 {
967 SCH_ITEM* driver = subgraph->m_driver;
968 SCH_SHEET_PATH sheet = subgraph->m_sheet;
969
970 switch( driver->Type() )
971 {
972 case SCH_LABEL_T:
973 case SCH_HIER_LABEL_T:
974 {
975 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
976 break;
977 }
979 {
980 m_global_label_cache[name].push_back( subgraph );
981 break;
982 }
983 case SCH_PIN_T:
984 {
985 SCH_PIN* pin = static_cast<SCH_PIN*>( driver );
986 wxASSERT( pin->IsPowerConnection() );
987 m_global_label_cache[name].push_back( subgraph );
988 break;
989 }
990 default:
991 {
993
994 wxLogTrace( ConnTrace, "Unexpected strong driver %s",
995 driver->GetSelectMenuText( &unitsProvider ) );
996 break;
997 }
998 }
999 }
1000 }
1001}
1002
1003
1005{
1006 // Generate subgraphs for invisible power pins. These will be merged with other subgraphs
1007 // on the same sheet in the next loop.
1008
1009 std::unordered_map<int, CONNECTION_SUBGRAPH*> invisible_pin_subgraphs;
1010
1011 for( const auto& it : m_invisible_power_pins )
1012 {
1013 SCH_SHEET_PATH sheet = it.first;
1014 SCH_PIN* pin = it.second;
1015
1016 if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParent()->IsPower() )
1017 {
1018 // ERC will warn about this: user has wired up an invisible pin
1019 continue;
1020 }
1021
1022 SCH_CONNECTION* connection = pin->GetOrInitConnection( sheet, this );
1023
1024 // If this pin already has a subgraph, don't need to process
1025 if( !connection || connection->SubgraphCode() > 0 )
1026 continue;
1027
1028 connection->SetName( pin->GetShownName() );
1029
1030 int code = assignNewNetCode( *connection );
1031
1032 connection->SetNetCode( code );
1033
1034 CONNECTION_SUBGRAPH* subgraph;
1035 auto jj = invisible_pin_subgraphs.find( code );
1036
1037 if( jj != invisible_pin_subgraphs.end() )
1038 {
1039 subgraph = jj->second;
1040 subgraph->AddItem( pin );
1041 }
1042 else
1043 {
1044 subgraph = new CONNECTION_SUBGRAPH( this );
1045
1046 subgraph->m_code = m_last_subgraph_code++;
1047 subgraph->m_sheet = sheet;
1048
1049 subgraph->AddItem( pin );
1050 subgraph->ResolveDrivers();
1051
1052 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(), code };
1053 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1054 m_subgraphs.push_back( subgraph );
1055 m_driver_subgraphs.push_back( subgraph );
1056
1057 invisible_pin_subgraphs[code] = subgraph;
1058 }
1059
1060 connection->SetSubgraphCode( subgraph->m_code );
1061 }
1062}
1063
1064
1066{
1067 // Here we do all the local (sheet) processing of each subgraph, including assigning net
1068 // codes, merging subgraphs together that use label connections, etc.
1069
1070 // Cache remaining valid subgraphs by sheet path
1071 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1072 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1073
1074 std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
1075
1076 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1077 {
1078 if( subgraph->m_absorbed )
1079 continue;
1080
1081 SCH_CONNECTION* connection = subgraph->m_driver_connection;
1082 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1083 wxString name = connection->Name();
1084
1085 // Test subgraphs with weak drivers for net name conflicts and fix them
1086 unsigned suffix = 1;
1087
1088 auto create_new_name =
1089 [&suffix]( SCH_CONNECTION* aConn ) -> wxString
1090 {
1091 wxString newName;
1092 wxString suffixStr = std::to_wstring( suffix );
1093
1094 // For group buses with a prefix, we can add the suffix to the prefix.
1095 // If they don't have a prefix, we force the creation of a prefix so that
1096 // two buses don't get inadvertently shorted together.
1097 if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
1098 {
1099 wxString prefix = aConn->BusPrefix();
1100
1101 if( prefix.empty() )
1102 prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
1103
1104 wxString oldName = aConn->Name().AfterFirst( '{' );
1105
1106 newName << prefix << wxT( "_" ) << suffixStr << wxT( "{" ) << oldName;
1107
1108 aConn->ConfigureFromLabel( newName );
1109 }
1110 else
1111 {
1112 newName << aConn->Name() << wxT( "_" ) << suffixStr;
1113 aConn->SetSuffix( wxString( wxT( "_" ) ) << suffixStr );
1114 }
1115
1116 suffix++;
1117 return newName;
1118 };
1119
1120 if( !subgraph->m_strong_driver )
1121 {
1122 std::vector<CONNECTION_SUBGRAPH*>* vec = &m_net_name_to_subgraphs_map.at( name );
1123
1124 // If we are a unique bus vector, check if we aren't actually unique because of another
1125 // subgraph with a similar bus vector
1126 if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1127 {
1128 wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
1129 vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
1130 }
1131
1132 if( vec->size() > 1 )
1133 {
1134 wxString new_name = create_new_name( connection );
1135
1136 while( m_net_name_to_subgraphs_map.count( new_name ) )
1137 new_name = create_new_name( connection );
1138
1139 wxLogTrace( ConnTrace, "%ld (%s) is weakly driven and not unique. Changing to %s.",
1140 subgraph->m_code, name, new_name );
1141
1142 alg::delete_matching( *vec, subgraph );
1143
1144 m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1145
1146 name = new_name;
1147 }
1148 else
1149 {
1150 // If there is no conflict, promote sheet pins to be strong drivers so that they
1151 // will be considered below for propagation/merging.
1152
1153 // It is possible for this to generate a conflict if the sheet pin has the same
1154 // name as a global label on the same sheet, because global merging will then treat
1155 // this subgraph as if it had a matching local label. So, for those cases, we
1156 // don't apply this promotion
1157
1158 if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1159 {
1160 bool conflict = false;
1161 wxString global_name = connection->Name( true );
1162 auto kk = m_net_name_to_subgraphs_map.find( global_name );
1163
1164 if( kk != m_net_name_to_subgraphs_map.end() )
1165 {
1166 // A global will conflict if it is on the same sheet as this subgraph, since
1167 // it would be connected by implicit local label linking
1168 std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
1169
1170 for( const CONNECTION_SUBGRAPH* candidate : candidates )
1171 {
1172 if( candidate->m_sheet == sheet )
1173 conflict = true;
1174 }
1175 }
1176
1177 if( conflict )
1178 {
1179 wxLogTrace( ConnTrace,
1180 "%ld (%s) skipped for promotion due to potential conflict",
1181 subgraph->m_code, name );
1182 }
1183 else
1184 {
1186
1187 wxLogTrace( ConnTrace,
1188 "%ld (%s) weakly driven by unique sheet pin %s, promoting",
1189 subgraph->m_code, name,
1190 subgraph->m_driver->GetSelectMenuText( &unitsProvider ) );
1191
1192 subgraph->m_strong_driver = true;
1193 }
1194 }
1195 }
1196 }
1197
1198 // Assign net codes
1199
1200 if( connection->IsBus() )
1201 {
1202 int code = -1;
1203 auto it = m_bus_name_to_code_map.find( name );
1204
1205 if( it != m_bus_name_to_code_map.end() )
1206 {
1207 code = it->second;
1208 }
1209 else
1210 {
1211 code = m_last_bus_code++;
1212 m_bus_name_to_code_map[ name ] = code;
1213 }
1214
1215 connection->SetBusCode( code );
1216 assignNetCodesToBus( connection );
1217 }
1218 else
1219 {
1220 assignNewNetCode( *connection );
1221 }
1222
1223 // Reset the flag for the next loop below
1224 subgraph->m_dirty = true;
1225
1226 // Next, we merge together subgraphs that have label connections, and create
1227 // neighbor links for subgraphs that are part of a bus on the same sheet.
1228 // For merging, we consider each possible strong driver.
1229
1230 // If this subgraph doesn't have a strong driver, let's skip it, since there is no
1231 // way it will be merged with anything.
1232
1233 if( !subgraph->m_strong_driver )
1234 continue;
1235
1236 // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
1237 // as the subgraph we are considering that has a strong driver.
1238 // Weakly driven subgraphs are not considered since they will never be absorbed or
1239 // form neighbor links.
1240
1241 std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
1242 std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
1243 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
1244 std::back_inserter( candidate_subgraphs ),
1245 [&] ( const CONNECTION_SUBGRAPH* candidate )
1246 {
1247 return ( !candidate->m_absorbed &&
1248 candidate->m_strong_driver &&
1249 candidate != subgraph );
1250 } );
1251
1252 // This is a list of connections on the current subgraph to compare to the
1253 // drivers of each candidate subgraph. If the current subgraph is a bus,
1254 // we should consider each bus member.
1255 std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
1256
1257 // Also check the main driving connection
1258 connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
1259
1260 auto add_connections_to_check =
1261 [&] ( CONNECTION_SUBGRAPH* aSubgraph )
1262 {
1263 for( SCH_ITEM* possible_driver : aSubgraph->m_items )
1264 {
1265 if( possible_driver == aSubgraph->m_driver )
1266 continue;
1267
1268 auto c = getDefaultConnection( possible_driver, aSubgraph );
1269
1270 if( c )
1271 {
1272 if( c->Type() != aSubgraph->m_driver_connection->Type() )
1273 continue;
1274
1275 if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
1276 continue;
1277
1278 connections_to_check.push_back( c );
1279 wxLogTrace( ConnTrace,
1280 "%lu (%s): Adding secondary driver %s", aSubgraph->m_code,
1281 aSubgraph->m_driver_connection->Name( true ),
1282 c->Name( true ) );
1283 }
1284 }
1285 };
1286
1287 // Now add other strong drivers
1288 // The actual connection attached to these items will have been overwritten
1289 // by the chosen driver of the subgraph, so we need to create a dummy connection
1290 add_connections_to_check( subgraph );
1291
1292 for( unsigned i = 0; i < connections_to_check.size(); i++ )
1293 {
1294 auto member = connections_to_check[i];
1295
1296 if( member->IsBus() )
1297 {
1298 connections_to_check.insert( connections_to_check.end(),
1299 member->Members().begin(),
1300 member->Members().end() );
1301 }
1302
1303 wxString test_name = member->Name( true );
1304
1305 for( CONNECTION_SUBGRAPH* candidate : candidate_subgraphs )
1306 {
1307 if( candidate->m_absorbed )
1308 continue;
1309
1310 bool match = false;
1311
1312 if( candidate->m_driver_connection->Name( true ) == test_name )
1313 {
1314 match = true;
1315 }
1316 else
1317 {
1318 if( !candidate->m_multiple_drivers )
1319 continue;
1320
1321 for( SCH_ITEM *driver : candidate->m_drivers )
1322 {
1323 if( driver == candidate->m_driver )
1324 continue;
1325
1326 // Sheet pins are not candidates for merging
1327 if( driver->Type() == SCH_SHEET_PIN_T )
1328 continue;
1329
1330 if( driver->Type() == SCH_PIN_T )
1331 {
1332 auto pin = static_cast<SCH_PIN*>( driver );
1333
1334 if( pin->IsPowerConnection() && pin->GetShownName() == test_name )
1335 {
1336 match = true;
1337 break;
1338 }
1339 }
1340 else
1341 {
1342 wxASSERT( driver->Type() == SCH_LABEL_T ||
1343 driver->Type() == SCH_GLOBAL_LABEL_T ||
1344 driver->Type() == SCH_HIER_LABEL_T );
1345
1346 if( subgraph->GetNameForDriver( driver ) == test_name )
1347 {
1348 match = true;
1349 break;
1350 }
1351 }
1352 }
1353 }
1354
1355 if( match )
1356 {
1357 if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
1358 {
1359 wxLogTrace( ConnTrace, "%lu (%s) has bus child %lu (%s)", subgraph->m_code,
1360 connection->Name(), candidate->m_code, member->Name() );
1361
1362 subgraph->m_bus_neighbors[member].insert( candidate );
1363 candidate->m_bus_parents[member].insert( subgraph );
1364 }
1365 else
1366 {
1367 wxLogTrace( ConnTrace, "%lu (%s) absorbs neighbor %lu (%s)",
1368 subgraph->m_code, connection->Name(),
1369 candidate->m_code, candidate->m_driver_connection->Name() );
1370
1371 // Candidate may have other non-chosen drivers we need to follow
1372 add_connections_to_check( candidate );
1373
1374 subgraph->Absorb( candidate );
1375 invalidated_subgraphs.insert( subgraph );
1376 }
1377 }
1378 }
1379 }
1380 }
1381
1382 // Update any subgraph that was invalidated above
1383 for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
1384 {
1385 if( subgraph->m_absorbed )
1386 continue;
1387
1388 subgraph->ResolveDrivers();
1389
1390 if( subgraph->m_driver_connection->IsBus() )
1391 assignNetCodesToBus( subgraph->m_driver_connection );
1392 else
1393 assignNewNetCode( *subgraph->m_driver_connection );
1394
1395 wxLogTrace( ConnTrace, "Re-resolving drivers for %lu (%s)", subgraph->m_code,
1396 subgraph->m_driver_connection->Name() );
1397 }
1398
1399}
1400
1401
1402// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
1403// to the same subgraph necessarily if it runs over and over again on the same
1404// sheet. We need:
1405//
1406// a) a cache of net/bus codes, like used before
1407// b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
1408// c) some way of trying to avoid changing net names. so we should keep track
1409// of the previous driver of a net, and if it comes down to choosing between
1410// equally-prioritized drivers, choose the one that already exists as a driver
1411// on some portion of the items.
1412
1413
1414void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* aChangedItemHandler )
1415{
1416 // Recache all bus aliases for later use
1417 wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) );
1418
1419 SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
1420
1421 for( unsigned i = 0; i < all_sheets.size(); i++ )
1422 {
1423 for( const std::shared_ptr<BUS_ALIAS>& alias : all_sheets[i].LastScreen()->GetBusAliases() )
1424 m_bus_alias_cache[ alias->GetName() ] = alias;
1425 }
1426
1427 PROF_TIMER sub_graph( "buildItemSubGraphs" );
1429
1430 if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
1431 sub_graph.Show();
1432
1433
1440
1442
1444
1445 PROF_TIMER proc_sub_graph( "ProcessSubGraphs" );
1447
1448 if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
1449 proc_sub_graph.Show();
1450
1451 // Absorbed subgraphs should no longer be considered
1452 alg::delete_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool
1453 {
1454 return candidate->m_absorbed;
1455 } );
1456
1457 // Store global subgraphs for later reference
1458 std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
1459 std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1460 std::back_inserter( global_subgraphs ),
1461 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1462 {
1463 return !candidate->m_local_driver;
1464 } );
1465
1466 // Recache remaining valid subgraphs by sheet path
1468
1469 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1470 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1471
1473
1474 tp.push_loop( m_driver_subgraphs.size(),
1475 [&]( const int a, const int b)
1476 {
1477 for( int ii = a; ii < b; ++ii )
1478 m_driver_subgraphs[ii]->UpdateItemConnections();
1479 });
1480 tp.wait_for_tasks();
1481
1482 // Next time through the subgraphs, we do some post-processing to handle things like
1483 // connecting bus members to their neighboring subgraphs, and then propagate connections
1484 // through the hierarchy
1485
1486 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1487 {
1488 if( !subgraph->m_dirty )
1489 continue;
1490
1491 wxLogTrace( ConnTrace, "Processing %lu (%s) for propagation", subgraph->m_code,
1492 subgraph->m_driver_connection->Name() );
1493
1494 // For subgraphs that are driven by a global (power port or label) and have more
1495 // than one global driver, we need to seek out other subgraphs driven by the
1496 // same name as the non-chosen driver and update them to match the chosen one.
1497
1498 if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
1499 {
1500 for( SCH_ITEM* driver : subgraph->m_drivers )
1501 {
1502 if( driver == subgraph->m_driver )
1503 continue;
1504
1505 wxString secondary_name = subgraph->GetNameForDriver( driver );
1506
1507 if( secondary_name == subgraph->m_driver_connection->Name() )
1508 continue;
1509
1510 bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
1512
1513 for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
1514 {
1515 if( candidate == subgraph )
1516 continue;
1517
1518 if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
1519 continue;
1520
1521 SCH_CONNECTION* conn = candidate->m_driver_connection;
1522
1523 if( conn->Name() == secondary_name )
1524 {
1525 wxLogTrace( ConnTrace, "Global %lu (%s) promoted to %s", candidate->m_code,
1526 conn->Name(), subgraph->m_driver_connection->Name() );
1527
1528 conn->Clone( *subgraph->m_driver_connection );
1529
1530 candidate->m_dirty = false;
1531 }
1532 }
1533 }
1534 }
1535
1536 // This call will handle descending the hierarchy and updating child subgraphs
1537 propagateToNeighbors( subgraph );
1538 }
1539
1540 // Handle buses that have been linked together somewhere by member (net) connections.
1541 // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
1542
1543 // For net subgraphs that have more than one bus parent, we need to ensure that those
1544 // buses are linked together in the final netlist. The final name of each bus might not
1545 // match the local name that was used to establish the parent-child relationship, because
1546 // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
1547 // we need to identify the appropriate bus members to link together (and their final names),
1548 // and then update all instances of the old name in the hierarchy.
1549
1550 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1551 {
1552 // All SGs should have been processed by propagateToNeighbors above
1553 wxASSERT_MSG( !subgraph->m_dirty, "Subgraph not processed by propagateToNeighbors!" );
1554
1555 if( subgraph->m_bus_parents.size() < 2 )
1556 continue;
1557
1558 SCH_CONNECTION* conn = subgraph->m_driver_connection;
1559
1560 wxLogTrace( ConnTrace, "%lu (%s) has multiple bus parents",
1561 subgraph->m_code, conn->Name() );
1562
1563 wxASSERT( conn->IsNet() );
1564
1565 for( const auto& ii : subgraph->m_bus_parents )
1566 {
1567 SCH_CONNECTION* link_member = ii.first.get();
1568
1569 for( CONNECTION_SUBGRAPH* parent : ii.second )
1570 {
1571 while( parent->m_absorbed )
1572 parent = parent->m_absorbed_by;
1573
1574 SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
1575
1576 if( !match )
1577 {
1578 wxLogTrace( ConnTrace, "Warning: could not match %s inside %lu (%s)",
1579 conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
1580 continue;
1581 }
1582
1583 if( conn->Name() != match->Name() )
1584 {
1585 wxString old_name = match->Name();
1586
1587 wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", parent->m_code,
1588 parent->m_driver_connection->Name(), old_name, conn->Name() );
1589
1590 match->Clone( *conn );
1591
1592 auto jj = m_net_name_to_subgraphs_map.find( old_name );
1593
1594 if( jj == m_net_name_to_subgraphs_map.end() )
1595 continue;
1596
1597 for( CONNECTION_SUBGRAPH* old_sg : jj->second )
1598 {
1599 while( old_sg->m_absorbed )
1600 old_sg = old_sg->m_absorbed_by;
1601
1602 old_sg->m_driver_connection->Clone( *conn );
1603 }
1604 }
1605 }
1606 }
1607 }
1608
1609
1610 auto updateItemConnectionsTask =
1611 [&]( CONNECTION_SUBGRAPH* subgraph ) -> size_t
1612 {
1613 // Make sure weakly-driven single-pin nets get the unconnected_ prefix
1614 if( !subgraph->m_strong_driver && subgraph->m_drivers.size() == 1 &&
1615 subgraph->m_driver->Type() == SCH_PIN_T )
1616 {
1617 SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
1618 wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
1619
1620 subgraph->m_driver_connection->ConfigureFromLabel( name );
1621 }
1622
1623 subgraph->m_dirty = false;
1624 subgraph->UpdateItemConnections();
1625
1626 // No other processing to do on buses
1627 if( subgraph->m_driver_connection->IsBus() )
1628 return 0;
1629
1630 // As a visual aid, we can check sheet pins that are driven by themselves to see
1631 // if they should be promoted to buses
1632
1633 if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1634 {
1635 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
1636
1637 if( SCH_SHEET* sheet = pin->GetParent() )
1638 {
1639 wxString pinText = pin->GetText();
1640 SCH_SCREEN* screen = sheet->GetScreen();
1641
1642 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
1643 {
1644 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
1645
1646 if( label->GetText() == pinText )
1647 {
1648 SCH_SHEET_PATH path = subgraph->m_sheet;
1649 path.push_back( sheet );
1650
1651 SCH_CONNECTION* parent_conn = label->Connection( &path );
1652
1653 if( parent_conn && parent_conn->IsBus() )
1654 subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
1655
1656 break;
1657 }
1658 }
1659
1660 if( subgraph->m_driver_connection->IsBus() )
1661 return 0;
1662 }
1663 }
1664
1665 return 1;
1666 };
1667
1668 tp.push_loop( m_driver_subgraphs.size(),
1669 [&]( const int a, const int b)
1670 {
1671 for( int ii = a; ii < b; ++ii )
1672 updateItemConnectionsTask( m_driver_subgraphs[ii] );
1673 });
1674 tp.wait_for_tasks();
1675
1678
1679 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1680 {
1681 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(),
1682 subgraph->m_driver_connection->NetCode() };
1683 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1684
1685 m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
1686 }
1687
1688 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Prj().GetProjectFile().m_NetSettings;
1689 std::map<wxString, wxString> oldAssignments = netSettings->m_NetClassLabelAssignments;
1690
1691 netSettings->m_NetClassLabelAssignments.clear();
1692
1693 auto dirtySubgraphs =
1694 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
1695 {
1696 if( aChangedItemHandler )
1697 {
1698 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
1699 {
1700 for( SCH_ITEM* item : subgraph->m_items )
1701 (*aChangedItemHandler)( item );
1702 }
1703 }
1704 };
1705
1706 auto checkNetclassDrivers =
1707 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
1708 {
1709 const CONNECTION_SUBGRAPH* driverSubgraph;
1710 wxString netclass;
1711
1712 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
1713 {
1714 for( SCH_ITEM* item : subgraph->m_items )
1715 {
1716 netclass = subgraph->GetNetclassForDriver( item );
1717
1718 if( !netclass.IsEmpty() )
1719 break;
1720 }
1721
1722 if( !netclass.IsEmpty() )
1723 {
1724 driverSubgraph = subgraph;
1725 break;
1726 }
1727 }
1728
1729 if( netclass.IsEmpty() )
1730 return;
1731
1732 const wxString netname = driverSubgraph->GetNetName();
1733
1734 if( driverSubgraph->m_driver_connection->IsBus() )
1735 {
1736 for( const auto& member : driverSubgraph->m_driver_connection->Members() )
1737 {
1738 netSettings->m_NetClassLabelAssignments[ member->Name() ] = netclass;
1739
1740 auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
1741
1742 if( ii != m_net_name_to_subgraphs_map.end() )
1743 dirtySubgraphs( ii->second );
1744 }
1745 }
1746
1747 netSettings->m_NetClassLabelAssignments[ netname ] = netclass;
1748
1749 if( oldAssignments[ netname ] != netclass )
1750 dirtySubgraphs( subgraphs );
1751 };
1752
1753 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
1754 checkNetclassDrivers( subgraphs );
1755}
1756
1757
1759{
1760 int code;
1761
1762 auto it = m_net_name_to_code_map.find( aConnection.Name() );
1763
1764 if( it == m_net_name_to_code_map.end() )
1765 {
1766 code = m_last_net_code++;
1767 m_net_name_to_code_map[ aConnection.Name() ] = code;
1768 }
1769 else
1770 {
1771 code = it->second;
1772 }
1773
1774 aConnection.SetNetCode( code );
1775
1776 return code;
1777}
1778
1779
1781{
1782 std::vector< std::shared_ptr<SCH_CONNECTION>>& connections_to_check( aConnection->Members() );
1783
1784 for( unsigned i = 0; i < connections_to_check.size(); i++ )
1785 {
1786 const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
1787
1788 if( member->IsBus() )
1789 {
1790 connections_to_check.insert( connections_to_check.end(),
1791 member->Members().begin(),
1792 member->Members().end() );
1793 continue;
1794 }
1795
1796 assignNewNetCode( *member );
1797 }
1798}
1799
1800
1802{
1803 SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
1804 std::vector<CONNECTION_SUBGRAPH*> search_list;
1805 std::unordered_set<CONNECTION_SUBGRAPH*> visited;
1806 std::vector<SCH_CONNECTION*> stale_bus_members;
1807
1808 auto visit =
1809 [&]( CONNECTION_SUBGRAPH* aParent )
1810 {
1811 for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
1812 {
1813 SCH_SHEET_PATH path = aParent->m_sheet;
1814 path.push_back( pin->GetParent() );
1815
1816 auto it = m_sheet_to_subgraphs_map.find( path );
1817
1818 if( it == m_sheet_to_subgraphs_map.end() )
1819 continue;
1820
1821 for( CONNECTION_SUBGRAPH* candidate : it->second )
1822 {
1823 if( !candidate->m_strong_driver
1824 || candidate->m_hier_ports.empty()
1825 || visited.count( candidate ) )
1826 {
1827 continue;
1828 }
1829
1830 for( SCH_HIERLABEL* label : candidate->m_hier_ports )
1831 {
1832 if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
1833 {
1834 wxLogTrace( ConnTrace, "%lu: found child %lu (%s)", aParent->m_code,
1835 candidate->m_code, candidate->m_driver_connection->Name() );
1836
1837 candidate->m_hier_parent = aParent;
1838
1839 search_list.push_back( candidate );
1840 break;
1841 }
1842 }
1843 }
1844 }
1845
1846 for( SCH_HIERLABEL* label : aParent->m_hier_ports )
1847 {
1848 SCH_SHEET_PATH path = aParent->m_sheet;
1849 path.pop_back();
1850
1851 auto it = m_sheet_to_subgraphs_map.find( path );
1852
1853 if( it == m_sheet_to_subgraphs_map.end() )
1854 continue;
1855
1856 for( CONNECTION_SUBGRAPH* candidate : it->second )
1857 {
1858 if( candidate->m_hier_pins.empty()
1859 || visited.count( candidate )
1860 || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
1861 {
1862 continue;
1863 }
1864
1865 const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
1866
1867 for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
1868 {
1869 // If the last sheet UUIDs won't match, no need to check the full path
1870 if( pin->GetParent()->m_Uuid != last_parent_uuid )
1871 continue;
1872
1873 SCH_SHEET_PATH pin_path = path;
1874 pin_path.push_back( pin->GetParent() );
1875
1876 if( pin_path != aParent->m_sheet )
1877 continue;
1878
1879 if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
1880 {
1881 wxLogTrace( ConnTrace, "%lu: found additional parent %lu (%s)",
1882 aParent->m_code, candidate->m_code,
1883 candidate->m_driver_connection->Name() );
1884
1885 search_list.push_back( candidate );
1886 break;
1887 }
1888 }
1889 }
1890 }
1891 };
1892
1893 auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
1894 for( const auto& kv : aParentGraph->m_bus_neighbors )
1895 {
1896 for( CONNECTION_SUBGRAPH* neighbor : kv.second )
1897 {
1898 // May have been absorbed but won't have been deleted
1899 while( neighbor->m_absorbed )
1900 neighbor = neighbor->m_absorbed_by;
1901
1902 SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
1903
1904 // Now member may be out of date, since we just cloned the
1905 // connection from higher up in the hierarchy. We need to
1906 // figure out what the actual new connection is.
1907 SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
1908
1909 if( !member )
1910 {
1911 // Try harder: we might match on a secondary driver
1912 for( CONNECTION_SUBGRAPH* sg : kv.second )
1913 {
1914 if( sg->m_multiple_drivers )
1915 {
1916 SCH_SHEET_PATH sheet = sg->m_sheet;
1917
1918 for( SCH_ITEM* driver : sg->m_drivers )
1919 {
1920 auto c = getDefaultConnection( driver, sg );
1921 member = matchBusMember( parent, c.get() );
1922
1923 if( member )
1924 break;
1925 }
1926 }
1927
1928 if( member )
1929 break;
1930 }
1931 }
1932
1933 // This is bad, probably an ERC error
1934 if( !member )
1935 {
1936 wxLogTrace( ConnTrace, "Could not match bus member %s in %s",
1937 kv.first->Name(), parent->Name() );
1938 continue;
1939 }
1940
1941 auto neighbor_conn = neighbor->m_driver_connection;
1942 auto neighbor_name = neighbor_conn->Name();
1943
1944 // Matching name: no update needed
1945 if( neighbor_name == member->Name() )
1946 continue;
1947
1948 // Was this neighbor already updated from a different sheet? Don't rename it again
1949 if( neighbor_conn->Sheet() != neighbor->m_sheet )
1950 continue;
1951
1952 // Safety check against infinite recursion
1953 wxASSERT( neighbor_conn->IsNet() );
1954
1955 wxLogTrace( ConnTrace, "%lu (%s) connected to bus member %s (local %s)",
1956 neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
1957
1958 // Take whichever name is higher priority
1961 {
1962 member->Clone( *neighbor_conn );
1963 stale_bus_members.push_back( member );
1964 }
1965 else
1966 {
1967 neighbor_conn->Clone( *member );
1968
1969 recacheSubgraphName( neighbor, neighbor_name );
1970
1971 // Recurse onto this neighbor in case it needs to re-propagate
1972 neighbor->m_dirty = true;
1973 propagateToNeighbors( neighbor );
1974 }
1975 }
1976 }
1977 };
1978
1979 // If we are a bus, we must propagate to local neighbors and then the hierarchy
1980 if( conn->IsBus() )
1981 propagate_bus_neighbors( aSubgraph );
1982
1983 // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
1984 // If we only have one or the other, process (we can either go bottom-up or top-down depending
1985 // on which subgraph comes up first)
1986 if( !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
1987 {
1988 wxLogTrace( ConnTrace, "%lu (%s) has both hier ports and pins; deferring processing",
1989 aSubgraph->m_code, conn->Name() );
1990 return;
1991 }
1992 else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
1993 {
1994 wxLogTrace( ConnTrace, "%lu (%s) has no hier pins or ports; marking clean",
1995 aSubgraph->m_code, conn->Name() );
1996 aSubgraph->m_dirty = false;
1997 return;
1998 }
1999
2000 visited.insert( aSubgraph );
2001
2002 wxLogTrace( ConnTrace, "Propagating %lu (%s) to subsheets",
2003 aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
2004
2005 visit( aSubgraph );
2006
2007 for( unsigned i = 0; i < search_list.size(); i++ )
2008 {
2009 auto child = search_list[i];
2010
2011 visited.insert( child );
2012
2013 visit( child );
2014
2015 child->m_dirty = false;
2016 }
2017
2018 // Now, find the best driver for this chain of subgraphs
2019 CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
2022 bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2023 wxString bestName = aSubgraph->m_driver_connection->Name();
2024
2025 // Check if a subsheet has a higher-priority connection to the same net
2027 {
2028 for( CONNECTION_SUBGRAPH* subgraph : visited )
2029 {
2030 if( subgraph == aSubgraph )
2031 continue;
2032
2034 CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
2035
2036 bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2037 wxString candidateName = subgraph->m_driver_connection->Name();
2038 bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
2039 bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
2040
2041 // Pick a better driving subgraph if it:
2042 // a) has a power pin or global driver
2043 // b) is a strong driver and we're a weak driver
2044 // c) is a higher priority strong driver
2045 // d) matches our priority, is a strong driver, and has a shorter path
2046 // e) matches our strength and is at least as short, and is alphabetically lower
2047
2048 if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
2049 ( !bestIsStrong && candidateStrong ) ||
2050 ( priority > highest && candidateStrong ) ||
2051 ( priority == highest && candidateStrong && shorterPath ) ||
2052 ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
2053 ( candidateName < bestName ) ) )
2054 {
2055 bestDriver = subgraph;
2056 highest = priority;
2057 bestIsStrong = candidateStrong;
2058 bestName = candidateName;
2059 }
2060 }
2061 }
2062
2063 if( bestDriver != aSubgraph )
2064 {
2065 wxLogTrace( ConnTrace, "%lu (%s) overridden by new driver %lu (%s)",
2066 aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
2067 bestDriver->m_driver_connection->Name() );
2068 }
2069
2070 conn = bestDriver->m_driver_connection;
2071
2072 for( CONNECTION_SUBGRAPH* subgraph : visited )
2073 {
2074 wxString old_name = subgraph->m_driver_connection->Name();
2075
2076 subgraph->m_driver_connection->Clone( *conn );
2077
2078 if( old_name != conn->Name() )
2079 recacheSubgraphName( subgraph, old_name );
2080
2081 if( conn->IsBus() )
2082 propagate_bus_neighbors( subgraph );
2083 }
2084
2085 // Somewhere along the way, a bus member may have been upgraded to a global or power label.
2086 // Because this can happen anywhere, we need a second pass to update all instances of that bus
2087 // member to have the correct connection info
2088 if( conn->IsBus() && !stale_bus_members.empty() )
2089 {
2090 for( SCH_CONNECTION* stale_member : stale_bus_members )
2091 {
2092 for( CONNECTION_SUBGRAPH* subgraph : visited )
2093 {
2094 SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
2095 stale_member );
2096
2097 if( !member )
2098 {
2099 wxLogTrace( ConnTrace, "WARNING: failed to match stale member %s in %s.",
2100 stale_member->Name(), subgraph->m_driver_connection->Name() );
2101 continue;
2102 }
2103
2104 wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", subgraph->m_code,
2105 subgraph->m_driver_connection->Name(), member->LocalName(),
2106 stale_member->Name() );
2107
2108 member->Clone( *stale_member );
2109
2110 propagate_bus_neighbors( subgraph );
2111 }
2112 }
2113 }
2114
2115 aSubgraph->m_dirty = false;
2116}
2117
2118
2119std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
2120 CONNECTION_SUBGRAPH* aSubgraph )
2121{
2122 std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
2123
2124 switch( aItem->Type() )
2125 {
2126 case SCH_PIN_T:
2127 {
2128 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
2129
2130 if( pin->IsPowerConnection() )
2131 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2132
2133 break;
2134 }
2135
2136 case SCH_GLOBAL_LABEL_T:
2137 case SCH_HIER_LABEL_T:
2138 case SCH_LABEL_T:
2139 {
2140 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
2141 break;
2142 }
2143
2144 default:
2145 break;
2146 }
2147
2148 if( c )
2149 {
2150 c->SetGraph( this );
2151 c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
2152 }
2153
2154 return c;
2155}
2156
2157
2159 SCH_CONNECTION* aSearch )
2160{
2161 wxASSERT( aBusConnection->IsBus() );
2162
2163 SCH_CONNECTION* match = nullptr;
2164
2165 if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
2166 {
2167 // Vector bus: compare against index, because we allow the name
2168 // to be different
2169
2170 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
2171 {
2172 if( bus_member->VectorIndex() == aSearch->VectorIndex() )
2173 {
2174 match = bus_member.get();
2175 break;
2176 }
2177 }
2178 }
2179 else
2180 {
2181 // Group bus
2182 for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
2183 {
2184 // Vector inside group: compare names, because for bus groups
2185 // we expect the naming to be consistent across all usages
2186 // TODO(JE) explain this in the docs
2187 if( c->Type() == CONNECTION_TYPE::BUS )
2188 {
2189 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
2190 {
2191 if( bus_member->LocalName() == aSearch->LocalName() )
2192 {
2193 match = bus_member.get();
2194 break;
2195 }
2196 }
2197 }
2198 else if( c->LocalName() == aSearch->LocalName() )
2199 {
2200 match = c.get();
2201 break;
2202 }
2203 }
2204 }
2205
2206 return match;
2207}
2208
2209
2211 const wxString& aOldName )
2212{
2213 auto it = m_net_name_to_subgraphs_map.find( aOldName );
2214
2215 if( it != m_net_name_to_subgraphs_map.end() )
2216 {
2217 std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
2218 alg::delete_matching( vec, aSubgraph );
2219 }
2220
2221 wxLogTrace( ConnTrace, "recacheSubgraphName: %s => %s", aOldName,
2222 aSubgraph->m_driver_connection->Name() );
2223
2224 m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
2225}
2226
2227
2228std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
2229{
2230 auto it = m_bus_alias_cache.find( aName );
2231
2232 return it != m_bus_alias_cache.end() ? it->second : nullptr;
2233}
2234
2235
2236std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
2237{
2238 std::vector<const CONNECTION_SUBGRAPH*> ret;
2239
2240 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
2241 {
2242 // Graph is supposed to be up-to-date before calling this
2243 wxASSERT( !subgraph->m_dirty );
2244
2245 if( !subgraph->m_driver )
2246 continue;
2247
2248 SCH_CONNECTION* connection = subgraph->m_driver->Connection( &subgraph->m_sheet );
2249
2250 if( !connection->IsBus() )
2251 continue;
2252
2253 auto labels = subgraph->GetBusLabels();
2254
2255 if( labels.size() > 1 )
2256 {
2257 bool different = false;
2258 wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
2259
2260 for( unsigned i = 1; i < labels.size(); ++i )
2261 {
2262 if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
2263 {
2264 different = true;
2265 break;
2266 }
2267 }
2268
2269 if( !different )
2270 continue;
2271
2272 wxLogTrace( ConnTrace, "SG %ld (%s) has multiple bus labels", subgraph->m_code,
2273 connection->Name() );
2274
2275 ret.push_back( subgraph );
2276 }
2277 }
2278
2279 return ret;
2280}
2281
2282
2284 const SCH_SHEET_PATH& aPath )
2285{
2286 auto it = m_net_name_to_subgraphs_map.find( aNetName );
2287
2288 if( it == m_net_name_to_subgraphs_map.end() )
2289 return nullptr;
2290
2291 for( CONNECTION_SUBGRAPH* sg : it->second )
2292 {
2293 // Cache is supposed to be valid by now
2294 wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
2295
2296 if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
2297 return sg;
2298 }
2299
2300 return nullptr;
2301}
2302
2303
2305{
2306 auto it = m_net_name_to_subgraphs_map.find( aNetName );
2307
2308 if( it == m_net_name_to_subgraphs_map.end() )
2309 return nullptr;
2310
2311 wxASSERT( !it->second.empty() );
2312
2313 return it->second[0];
2314}
2315
2316
2318{
2319 auto it = m_item_to_subgraph_map.find( aItem );
2320 CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
2321
2322 while( ret && ret->m_absorbed )
2323 ret = ret->m_absorbed_by;
2324
2325 return ret;
2326}
2327
2328
2330{
2331 int error_count = 0;
2332
2333 wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::RunERC" );
2334
2335 ERC_SETTINGS& settings = m_schematic->ErcSettings();
2336
2337 // We don't want to run many ERC checks more than once on a given screen even though it may
2338 // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
2339 std::set<SCH_ITEM*> seenDriverInstances;
2340
2341 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
2342 {
2343 // There shouldn't be any null sub-graph pointers.
2344 wxCHECK2( subgraph, continue );
2345
2346 // Graph is supposed to be up-to-date before calling RunERC()
2347 wxASSERT( !subgraph->m_dirty );
2348
2349 if( subgraph->m_absorbed )
2350 continue;
2351
2352 if( seenDriverInstances.count( subgraph->m_driver ) )
2353 continue;
2354
2355 if( subgraph->m_driver )
2356 seenDriverInstances.insert( subgraph->m_driver );
2357
2368 if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
2369 {
2370 if( !ercCheckMultipleDrivers( subgraph ) )
2371 error_count++;
2372 }
2373
2374 subgraph->ResolveDrivers( false );
2375
2376 if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
2377 {
2378 if( !ercCheckBusToNetConflicts( subgraph ) )
2379 error_count++;
2380 }
2381
2382 if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
2383 {
2384 if( !ercCheckBusToBusEntryConflicts( subgraph ) )
2385 error_count++;
2386 }
2387
2388 if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
2389 {
2390 if( !ercCheckBusToBusConflicts( subgraph ) )
2391 error_count++;
2392 }
2393
2394 if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
2395 {
2396 if( !ercCheckFloatingWires( subgraph ) )
2397 error_count++;
2398 }
2399
2402 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2403 {
2404 if( !ercCheckNoConnects( subgraph ) )
2405 error_count++;
2406 }
2407
2409 || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
2410 {
2411 if( !ercCheckLabels( subgraph ) )
2412 error_count++;
2413 }
2414 }
2415
2416 // Hierarchical sheet checking is done at the schematic level
2418 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2419 {
2420 error_count += ercCheckHierSheets();
2421 }
2422
2423 if( settings.IsTestEnabled( ERCE_NETCLASS_CONFLICT ) )
2424 {
2425 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
2426 {
2427 if( !ercCheckNetclassConflicts( subgraphs ) )
2428 error_count++;
2429 }
2430 }
2431
2432 return error_count;
2433}
2434
2435
2437{
2438 wxCHECK( aSubgraph, false );
2439 /*
2440 * This was changed late in 6.0 to fix https://gitlab.com/kicad/code/kicad/-/issues/9367
2441 * so I'm going to leave the original code in for just a little while. If anyone comes
2442 * across this in 7.0 development (or later), feel free to delete.
2443 */
2444#if 0
2445 if( aSubgraph->m_second_driver )
2446 {
2447 SCH_ITEM* primary = aSubgraph->m_first_driver;
2448 SCH_ITEM* secondary = aSubgraph->m_second_driver;
2449
2450 wxPoint pos = primary->Type() == SCH_PIN_T ?
2451 static_cast<SCH_PIN*>( primary )->GetTransformedPosition() :
2452 primary->GetPosition();
2453
2454 wxString primaryName = aSubgraph->GetNameForDriver( primary );
2455 wxString secondaryName = aSubgraph->GetNameForDriver( secondary );
2456
2457 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
2458 "items; %s will be used in the netlist" ),
2459 primaryName, secondaryName, primaryName );
2460
2461 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
2462 ercItem->SetItems( primary, secondary );
2463 ercItem->SetErrorMessage( msg );
2464
2465 SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
2466 aSubgraph->m_sheet.LastScreen()->Append( marker );
2467
2468 return false;
2469 }
2470#else
2471 if( aSubgraph->m_multiple_drivers )
2472 {
2473 for( SCH_ITEM* driver : aSubgraph->m_drivers )
2474 {
2475 if( driver == aSubgraph->m_driver )
2476 continue;
2477
2478 if( driver->Type() == SCH_GLOBAL_LABEL_T
2479 || driver->Type() == SCH_HIER_LABEL_T
2480 || driver->Type() == SCH_LABEL_T
2481 || ( driver->Type() == SCH_PIN_T
2482 && static_cast<SCH_PIN*>( driver )->IsPowerConnection() ) )
2483 {
2484 wxString primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
2485 wxString secondaryName = aSubgraph->GetNameForDriver( driver );
2486
2487 if( primaryName == secondaryName )
2488 continue;
2489
2490 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
2491 "items; %s will be used in the netlist" ),
2492 primaryName, secondaryName, primaryName );
2493
2494 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
2495 ercItem->SetItems( aSubgraph->m_driver, driver );
2496 ercItem->SetErrorMessage( msg );
2497
2498 SCH_MARKER* marker = new SCH_MARKER( ercItem, driver->GetPosition() );
2499 aSubgraph->m_sheet.LastScreen()->Append( marker );
2500
2501 return false;
2502 }
2503 }
2504 }
2505#endif
2506
2507 return true;
2508}
2509
2510
2511bool CONNECTION_GRAPH::ercCheckNetclassConflicts( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2512{
2513 wxString firstNetclass;
2514 SCH_ITEM* firstNetclassDriver = nullptr;
2515
2516 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2517 {
2518 for( SCH_ITEM* item : subgraph->m_items )
2519 {
2520 const wxString netclass = subgraph->GetNetclassForDriver( item );
2521
2522 if( netclass.IsEmpty() )
2523 continue;
2524
2525 if( netclass != firstNetclass )
2526 {
2527 if( !firstNetclassDriver )
2528 {
2529 firstNetclass = netclass;
2530 firstNetclassDriver = item;
2531 continue;
2532 }
2533
2534 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NETCLASS_CONFLICT );
2535 ercItem->SetItems( firstNetclassDriver, item );
2536
2537 SCH_MARKER* marker = new SCH_MARKER( ercItem, item->GetPosition() );
2538 subgraph->m_sheet.LastScreen()->Append( marker );
2539
2540 return false;
2541 }
2542 }
2543 }
2544
2545 return true;
2546}
2547
2548
2550{
2551 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2552 SCH_SCREEN* screen = sheet.LastScreen();
2553
2554 SCH_ITEM* net_item = nullptr;
2555 SCH_ITEM* bus_item = nullptr;
2556 SCH_CONNECTION conn( this );
2557
2558 for( SCH_ITEM* item : aSubgraph->m_items )
2559 {
2560 switch( item->Type() )
2561 {
2562 case SCH_LINE_T:
2563 {
2564 if( item->GetLayer() == LAYER_BUS )
2565 bus_item = ( !bus_item ) ? item : bus_item;
2566 else
2567 net_item = ( !net_item ) ? item : net_item;
2568 break;
2569 }
2570
2571 case SCH_LABEL_T:
2572 case SCH_GLOBAL_LABEL_T:
2573 case SCH_SHEET_PIN_T:
2574 case SCH_HIER_LABEL_T:
2575 {
2576 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2577 conn.ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
2578
2579 if( conn.IsBus() )
2580 bus_item = ( !bus_item ) ? item : bus_item;
2581 else
2582 net_item = ( !net_item ) ? item : net_item;
2583 break;
2584 }
2585
2586 default:
2587 break;
2588 }
2589 }
2590
2591 if( net_item && bus_item )
2592 {
2593 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
2594 ercItem->SetItems( net_item, bus_item );
2595
2596 SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
2597 screen->Append( marker );
2598
2599 return false;
2600 }
2601
2602 return true;
2603}
2604
2605
2607{
2608 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2609 SCH_SCREEN* screen = sheet.LastScreen();
2610
2611 SCH_ITEM* label = nullptr;
2612 SCH_ITEM* port = nullptr;
2613
2614 for( SCH_ITEM* item : aSubgraph->m_items )
2615 {
2616 switch( item->Type() )
2617 {
2618 case SCH_TEXT_T:
2619 case SCH_GLOBAL_LABEL_T:
2620 {
2621 if( !label && item->Connection( &sheet )->IsBus() )
2622 label = item;
2623 break;
2624 }
2625
2626 case SCH_SHEET_PIN_T:
2627 case SCH_HIER_LABEL_T:
2628 {
2629 if( !port && item->Connection( &sheet )->IsBus() )
2630 port = item;
2631 break;
2632 }
2633
2634 default:
2635 break;
2636 }
2637 }
2638
2639 if( label && port )
2640 {
2641 bool match = false;
2642
2643 for( const auto& member : label->Connection( &sheet )->Members() )
2644 {
2645 for( const auto& test : port->Connection( &sheet )->Members() )
2646 {
2647 if( test != member && member->Name() == test->Name() )
2648 {
2649 match = true;
2650 break;
2651 }
2652 }
2653
2654 if( match )
2655 break;
2656 }
2657
2658 if( !match )
2659 {
2660 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
2661 ercItem->SetItems( label, port );
2662
2663 SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
2664 screen->Append( marker );
2665
2666 return false;
2667 }
2668 }
2669
2670 return true;
2671}
2672
2673
2675{
2676 bool conflict = false;
2677 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2678 SCH_SCREEN* screen = sheet.LastScreen();
2679
2680 SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2681 SCH_ITEM* bus_wire = nullptr;
2682 wxString bus_name;
2683
2684 if( !aSubgraph->m_driver_connection )
2685 {
2686 // Incomplete bus entry. Let the unconnected tests handle it.
2687 return true;
2688 }
2689
2690 for( SCH_ITEM* item : aSubgraph->m_items )
2691 {
2692 switch( item->Type() )
2693 {
2695 {
2696 if( !bus_entry )
2697 bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2698 break;
2699 }
2700
2701 default:
2702 break;
2703 }
2704 }
2705
2706 if( bus_entry && bus_entry->m_connected_bus_item )
2707 {
2708 bus_wire = bus_entry->m_connected_bus_item;
2709
2710 wxASSERT( bus_wire->Type() == SCH_LINE_T );
2711
2712 // In some cases, the connection list (SCH_CONNECTION*) can be null.
2713 // Skip null connections.
2714 if( bus_entry->Connection( &sheet )
2715 && bus_wire->Type() == SCH_LINE_T
2716 && bus_wire->Connection( &sheet ) )
2717 {
2718 conflict = true; // Assume a conflict; we'll reset if we find it's OK
2719
2720 bus_name = bus_wire->Connection( &sheet )->Name();
2721
2722 std::set<wxString> test_names;
2723 test_names.insert( bus_entry->Connection( &sheet )->Name() );
2724
2725 wxString baseName = sheet.PathHumanReadable();
2726
2727 for( SCH_ITEM* driver : aSubgraph->m_drivers )
2728 test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
2729
2730 for( const auto& member : bus_wire->Connection( &sheet )->Members() )
2731 {
2732 if( member->Type() == CONNECTION_TYPE::BUS )
2733 {
2734 for( const auto& sub_member : member->Members() )
2735 {
2736 if( test_names.count( sub_member->Name() ) )
2737 conflict = false;
2738 }
2739 }
2740 else if( test_names.count( member->Name() ) )
2741 {
2742 conflict = false;
2743 }
2744 }
2745 }
2746 }
2747
2748 // Don't report warnings if this bus member has been overridden by a higher priority power pin
2749 // or global label
2750 if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
2752 {
2753 conflict = false;
2754 }
2755
2756 if( conflict )
2757 {
2758 wxString netName = aSubgraph->m_driver_connection->Name();
2759 wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
2760 " member of that bus" ),
2761 UnescapeString( netName ),
2762 UnescapeString( bus_name ) );
2763 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
2764 ercItem->SetItems( bus_entry, bus_wire );
2765 ercItem->SetErrorMessage( msg );
2766
2767 SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
2768 screen->Append( marker );
2769
2770 return false;
2771 }
2772
2773 return true;
2774}
2775
2776
2777// TODO(JE) Check sheet pins here too?
2779{
2780 ERC_SETTINGS& settings = m_schematic->ErcSettings();
2781 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2782 SCH_SCREEN* screen = sheet.LastScreen();
2783 bool ok = true;
2784
2785 if( aSubgraph->m_no_connect != nullptr )
2786 {
2787 SCH_PIN* pin = nullptr;
2788 std::set<SCH_ITEM*> unique_items;
2789
2790 // Any subgraph that contains both a pin and a no-connect should not
2791 // contain any other driving items.
2792
2793 for( SCH_ITEM* item : aSubgraph->m_items )
2794 {
2795 switch( item->Type() )
2796 {
2797 case SCH_PIN_T:
2798 {
2799 pin = static_cast<SCH_PIN*>( item );
2800
2801 // Insert the pin's parent so that we don't flag stacked pins
2802 if( auto [existing, success] = unique_items.insert(
2803 static_cast<SCH_ITEM*>( pin->GetParent() ) ); !success )
2804 {
2805 SCH_PIN* ex_pin = static_cast<SCH_PIN*>( *existing );
2806
2807 // Stacked pins don't count as connected
2808 // but if they are not stacked, but still in the same symbol
2809 // flag this for an error
2810 if( !pin->IsStacked( ex_pin ) )
2811 unique_items.insert( ex_pin );
2812 }
2813
2814 break;
2815 }
2816
2817 case SCH_LINE_T:
2818 case SCH_JUNCTION_T:
2819 case SCH_NO_CONNECT_T:
2820 break;
2821
2822 default:
2823 unique_items.insert( item );
2824 }
2825 }
2826
2827 if( unique_items.size() > 1 && pin && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
2828 {
2829 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
2830 ercItem->SetItems( pin );
2831
2832 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2833 screen->Append( marker );
2834
2835 ok = false;
2836 }
2837
2838 if( unique_items.empty() && settings.IsTestEnabled( ERCE_NOCONNECT_NOT_CONNECTED ) )
2839 {
2840 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
2841 ercItem->SetItems( aSubgraph->m_no_connect );
2842
2843 SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
2844 screen->Append( marker );
2845
2846 ok = false;
2847 }
2848 }
2849 else
2850 {
2851 bool has_other_connections = false;
2852 std::vector<SCH_PIN*> pins;
2853
2854 // Any subgraph that lacks a no-connect and contains a pin should also
2855 // contain at least one other potential driver
2856
2857 for( SCH_ITEM* item : aSubgraph->m_items )
2858 {
2859 switch( item->Type() )
2860 {
2861 case SCH_PIN_T:
2862 {
2863 // Stacked pins do not count as other connections but non-stacked pins do
2864 if( !has_other_connections && !pins.empty() )
2865 {
2866 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
2867
2868 for( SCH_PIN* other_pin : pins )
2869 {
2870 if( other_pin->GetParent() != pin->GetParent()
2871 || other_pin->GetPosition() != pin->GetPosition() )
2872 {
2873 has_other_connections = true;
2874 break;
2875 }
2876 }
2877 }
2878
2879 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
2880
2881 break;
2882 }
2883
2884 default:
2885 if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
2886 has_other_connections = true;
2887
2888 break;
2889 }
2890 }
2891
2892 // For many checks, we can just use the first pin
2893 SCH_PIN* pin = pins.empty() ? nullptr : pins[0];
2894
2895 // Check if invisible power input pins connect to anything else via net name,
2896 // but not for power symbols as the ones in the standard library all have invisible pins
2897 // and we want to throw unconnected errors for those even if they are connected to other
2898 // net items by name, because usually failing to connect them graphically is a mistake
2899 if( pin && !has_other_connections
2900 && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
2901 && !pin->IsVisible()
2902 && !pin->GetLibPin()->GetParent()->IsPower() )
2903 {
2904 wxString name = pin->Connection( &sheet )->Name();
2905 wxString local_name = pin->Connection( &sheet )->Name( true );
2906
2907 if( m_global_label_cache.count( name )
2908 || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
2909 {
2910 has_other_connections = true;
2911 }
2912 }
2913
2914 // Only one pin, and it's not a no-connect pin
2915 if( pin && !has_other_connections
2916 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
2917 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
2918 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2919 {
2920 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2921 ercItem->SetItems( pin );
2922
2923 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2924 screen->Append( marker );
2925
2926 ok = false;
2927 }
2928
2929 // If there are multiple pins in this SG, they might be indirectly connected (by netname)
2930 // rather than directly connected (by wires). We want to flag dangling pins even if they
2931 // join nets with another pin, as it's often a mistake
2932 if( pins.size() > 1 )
2933 {
2934 for( SCH_PIN* testPin : pins )
2935 {
2936 // We only apply this test to power symbols, because other symbols have invisible
2937 // pins that are meant to be dangling, but the KiCad standard library power symbols
2938 // have invisible pins that are *not* meant to be dangling.
2939 if( testPin->GetLibPin()->GetParent()->IsPower()
2940 && testPin->ConnectedItems( sheet ).empty()
2941 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2942 {
2943 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2944 ercItem->SetItems( testPin );
2945
2946 SCH_MARKER* marker = new SCH_MARKER( ercItem,
2947 testPin->GetTransformedPosition() );
2948 screen->Append( marker );
2949
2950 ok = false;
2951 }
2952 }
2953 }
2954 }
2955
2956 return ok;
2957}
2958
2959
2961{
2962 if( aSubgraph->m_driver )
2963 return true;
2964
2965 std::vector<SCH_ITEM*> wires;
2966
2967 // We've gotten this far, so we know we have no valid driver. All we need to do is check
2968 // for a wire that we can place the error on.
2969
2970 for( SCH_ITEM* item : aSubgraph->m_items )
2971 {
2972 if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
2973 wires.emplace_back( item );
2974 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
2975 wires.emplace_back( item );
2976 }
2977
2978 if( !wires.empty() )
2979 {
2980 SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
2981
2982 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
2983 ercItem->SetItems( wires[0],
2984 wires.size() > 1 ? wires[1] : nullptr,
2985 wires.size() > 2 ? wires[2] : nullptr,
2986 wires.size() > 3 ? wires[3] : nullptr );
2987
2988 SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
2989 screen->Append( marker );
2990
2991 return false;
2992 }
2993
2994 return true;
2995}
2996
2997
2999{
3000 // Label connection rules:
3001 // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
3002 // Local labels are flagged if they don't connect to any pins and don't have a no-connect
3003 // Global labels are flagged if they appear only once, don't connect to any local labels,
3004 // and don't have a no-connect marker
3005
3006
3007 if( !aSubgraph->m_driver_connection )
3008 return true;
3009
3010 // Buses are excluded from this test: many users create buses with only a single instance
3011 // and it's not really a problem as long as the nets in the bus pass ERC
3012 if( aSubgraph->m_driver_connection->IsBus() )
3013 return true;
3014
3015 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3016 bool ok = true;
3017 int pinCount = 0;
3018
3019 std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
3020
3021
3022 auto hasPins =
3023 []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> int
3024 {
3025 int retval = 0;
3026
3027 for( const SCH_ITEM* item : aLocSubgraph->m_items )
3028 {
3029 switch( item->Type() )
3030 {
3031 case SCH_PIN_T:
3032 ++retval;
3033 break;
3034
3035 default: break;
3036 }
3037 }
3038
3039 return retval;
3040 };
3041
3042 auto reportError = [&]( SCH_TEXT* aText, int errCode )
3043 {
3044 if( settings.IsTestEnabled( errCode ) )
3045 {
3046 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
3047 ercItem->SetItems( aText );
3048
3049 SCH_MARKER* marker = new SCH_MARKER( ercItem, aText->GetPosition() );
3050 aSubgraph->m_sheet.LastScreen()->Append( marker );
3051 }
3052 };
3053
3054 pinCount = hasPins( aSubgraph );
3055
3056 for( SCH_ITEM* item : aSubgraph->m_items )
3057 {
3058 switch( item->Type() )
3059 {
3060 case SCH_LABEL_T:
3061 case SCH_GLOBAL_LABEL_T:
3062 case SCH_HIER_LABEL_T:
3063 {
3064 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3065
3066 label_map[item->Type()].push_back( text );
3067
3068 // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
3069 // we want to error if an individual label in the subgraph is floating, even if it's
3070 // connected to other valid things by way of another label on the same sheet.
3071 if( text->IsDangling() )
3072 {
3073 reportError( text, ERCE_LABEL_NOT_CONNECTED );
3074 return false;
3075 }
3076
3077 break;
3078 }
3079
3080 // If this subgraph has a no-connect, do not continue processing as we do not
3081 // submit no-connect errors for nets explicitly designated as no-connect
3082 case SCH_NO_CONNECT_T:
3083 return true;
3084
3085 default:
3086 break;
3087 }
3088 }
3089
3090 if( label_map.empty() )
3091 return true;
3092
3093 // This is a hacky way to find the true subgraph net name (why do we not store it?)
3094 // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
3095 wxString netName = aSubgraph->GetNetName();
3096
3097 for( auto it = m_net_name_to_subgraphs_map.begin(); it != m_net_name_to_subgraphs_map.end(); ++it )
3098 {
3099 for( CONNECTION_SUBGRAPH* graph : it->second )
3100 {
3101 if( graph == aSubgraph )
3102 {
3103 netName = it->first;
3104 }
3105 }
3106 }
3107
3108 wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
3109
3110 // Labels that have multiple pins connected are not dangling (may be used for naming segments)
3111 // so leave them without errors here
3112 if( pinCount > 1 )
3113 return true;
3114
3115 for( auto& [type, label_vec] : label_map )
3116 {
3117
3118 switch( type )
3119 {
3120 case SCH_GLOBAL_LABEL_T:
3121 if( !settings.IsTestEnabled( ERCE_GLOBLABEL ) )
3122 continue;
3123
3124 break;
3125 default:
3126 if( !settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
3127 continue;
3128
3129 break;
3130 }
3131
3132 for( SCH_TEXT* text : label_vec )
3133 {
3134 int allPins = pinCount;
3135
3136 auto it = m_net_name_to_subgraphs_map.find( netName );
3137
3138 if( it != m_net_name_to_subgraphs_map.end() )
3139 {
3140 for( const CONNECTION_SUBGRAPH* neighbor : it->second )
3141 {
3142 if( neighbor == aSubgraph )
3143 continue;
3144
3145 allPins += hasPins( neighbor );
3146 }
3147 }
3148
3149 if( allPins < 2 )
3150 {
3151 reportError( text,
3153 ok = false;
3154 }
3155 }
3156 }
3157
3158 return ok;
3159}
3160
3161
3163{
3164 int errors = 0;
3165
3166 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3167
3168 for( const SCH_SHEET_PATH& sheet : m_sheetList )
3169 {
3170 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
3171 {
3172 if( item->Type() != SCH_SHEET_T )
3173 continue;
3174
3175 SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
3176
3177 std::map<wxString, SCH_SHEET_PIN*> pins;
3178 std::map<wxString, SCH_HIERLABEL*> labels;
3179
3180 for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
3181 {
3182 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
3183 pins[pin->GetText()] = pin;
3184
3185 if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3186 {
3187 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3188 ercItem->SetItems( pin );
3189
3190 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
3191 sheet.LastScreen()->Append( marker );
3192
3193 errors++;
3194 }
3195 }
3196
3197 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
3198 {
3199 std::set<wxString> matchedPins;
3200
3201 for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
3202 {
3203 if( subItem->Type() == SCH_HIER_LABEL_T )
3204 {
3205 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
3206
3207 if( !pins.count( label->GetText() ) )
3208 labels[label->GetText()] = label;
3209 else
3210 matchedPins.insert( label->GetText() );
3211 }
3212 }
3213
3214 for( const wxString& matched : matchedPins )
3215 pins.erase( matched );
3216
3217 for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
3218 {
3219 wxString msg = wxString::Format( _( "Sheet pin %s has no matching hierarchical "
3220 "label inside the sheet" ),
3221 UnescapeString( unmatched.first ) );
3222
3223 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
3224 ercItem->SetItems( unmatched.second );
3225 ercItem->SetErrorMessage( msg );
3226 ercItem->SetIsSheetSpecific();
3227
3228 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
3229 sheet.LastScreen()->Append( marker );
3230
3231 errors++;
3232 }
3233
3234 for( const std::pair<const wxString, SCH_HIERLABEL*>& unmatched : labels )
3235 {
3236 wxString msg = wxString::Format( _( "Hierarchical label %s has no matching "
3237 "sheet pin in the parent sheet" ),
3238 UnescapeString( unmatched.first ) );
3239
3240 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
3241 ercItem->SetItems( unmatched.second );
3242 ercItem->SetErrorMessage( msg );
3243 ercItem->SetIsSheetSpecific();
3244
3245 SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
3246 parentSheet->GetScreen()->Append( marker );
3247
3248 errors++;
3249 }
3250 }
3251 }
3252 }
3253
3254 return errors;
3255}
const char * name
Definition: DXF_plotter.cpp:56
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:111
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
int RunERC()
Runs electrical rule checks on the connectivity graph.
static bool m_allowRealTime
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 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
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)
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph)
Updates all neighbors of a subgraph with this one's connectivity info.
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
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
void generateInvisiblePinSubGraphs()
Iterate through the invisible power pins to collect the global labels as drivers.
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
bool ercCheckNoConnects(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for proper presence or absence of no-connect symbols.
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)
std::unordered_map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map
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 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.
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
SCH_ITEM * m_no_connect
No-connect item in graph, if any.
const wxString & GetNameForDriver(SCH_ITEM *aItem)
Returns the candidate net name for a driver.
std::vector< SCH_ITEM * > m_drivers
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Return the priority (higher is more important) of a candidate driver.
SCH_SHEET_PATH m_sheet
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::unordered_map< SCH_ITEM *, wxString > m_driver_name_cache
A cache of escaped netnames from schematic items.
wxString GetNetName() const
Returns the fully-qualified net name for this subgraph (if one exists)
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.
void AddItem(SCH_ITEM *aItem)
Adds a new item to the subgraph.
std::vector< SCH_ITEM * > GetBusLabels() const
Returns all the bus labels attached to this subgraph (if any)
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.
std::vector< SCH_HIERLABEL * > m_hier_ports
std::vector< SCH_SHEET_PIN * > m_hier_pins
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.
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:251
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
virtual wxString GetSelectMenuText(UNITS_PROVIDER *aUnitsProvider) const
Return the text to display to be used in the selection clarification context menu when multiple items...
Definition: eda_item.cpp:108
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:227
Container for ERC settings.
Definition: erc_settings.h:114
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:130
Definition: kiid.h:48
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:86
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:76
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:179
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.
long VectorIndex() const
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
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:193
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:246
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction)
Definition: sch_item.h:444
void SetConnectivityDirty(bool aDirty=true)
Definition: sch_item.h:415
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:146
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 IsPowerConnection() const
Definition: sch_pin.h:158
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:433
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:145
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...
SCH_SCREEN * LastScreen()
wxString PathHumanReadable(bool aUseShortRootName=true) const
Return the sheet path in a human readable form made from the sheet names.
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:55
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:104
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:173
Schematic symbol object.
Definition: sch_symbol.h:80
int GetUnit() const
Definition: sch_symbol.h:227
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve a list of the SCH_PINs for the given sheet path.
Definition: sch_symbol.cpp:989
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:716
wxString GetShownText(int aDepth=0, bool aAllowExtraText=true) const override
Return the string actually shown after processing of the base text.
Definition: sch_text.cpp:342
VECTOR2I GetPosition() const override
Definition: sch_text.h:203
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:344
@ LAYER_BUS
Definition: layer_ids.h:345
@ LAYER_JUNCTION
Definition: layer_ids.h:346
@ LAYER_BUS_JUNCTION
Definition: layer_ids.h:384
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
@ PT_NC
not connected (must be left open)
@ PT_NIC
not internally connected (may be connected to anything)
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ BUS
This item represents a bus vector.
@ NET
This item represents a net.
@ BUS_GROUP
This item represents a bus group.
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.
@ L_OUTPUT
Definition: sch_text.h:98
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:146
@ SCH_NO_CONNECT_T
Definition: typeinfo.h:143
@ SCH_SYMBOL_T
Definition: typeinfo.h:156
@ SCH_FIELD_T
Definition: typeinfo.h:155
@ SCH_LABEL_T
Definition: typeinfo.h:151
@ SCH_SHEET_T
Definition: typeinfo.h:158
@ SCH_HIER_LABEL_T
Definition: typeinfo.h:153
@ SCH_BUS_BUS_ENTRY_T
Definition: typeinfo.h:145
@ SCH_SHEET_PIN_T
Definition: typeinfo.h:157
@ SCH_TEXT_T
Definition: typeinfo.h:150
@ SCH_BUS_WIRE_ENTRY_T
Definition: typeinfo.h:144
@ SCH_GLOBAL_LABEL_T
Definition: typeinfo.h:152
@ SCH_JUNCTION_T
Definition: typeinfo.h:142
@ SCH_PIN_T
Definition: typeinfo.h:159
Functions to provide common constants and other functions to assist in making a consistent UI.