KiCad PCB EDA Suite
CONNECTION_GRAPH Class Reference

Calculates the connectivity of a schematic and generates netlists. More...

#include <connection_graph.h>

Public Member Functions

 CONNECTION_GRAPH (SCHEMATIC *aSchematic=nullptr)
 
 ~CONNECTION_GRAPH ()
 
void Reset ()
 
void SetSchematic (SCHEMATIC *aSchematic)
 
void Recalculate (const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false)
 Updates the connection graph for the given list of sheets. More...
 
std::shared_ptr< BUS_ALIASGetBusAlias (const wxString &aName)
 Returns a bus alias pointer for the given name if it exists (from cache) More...
 
std::vector< const CONNECTION_SUBGRAPH * > GetBusesNeedingMigration ()
 Determines which subgraphs have more than one conflicting bus label. More...
 
int RunERC ()
 Runs electrical rule checks on the connectivity graph. More...
 
const NET_MAPGetNetMap () const
 
CONNECTION_SUBGRAPHFindSubgraphByName (const wxString &aNetName, const SCH_SHEET_PATH &aPath)
 Returns the subgraph for a given net name on a given sheet. More...
 
CONNECTION_SUBGRAPHFindFirstSubgraphByName (const wxString &aNetName)
 Retrieves a subgraph for the given net name, if one exists. More...
 
CONNECTION_SUBGRAPHGetSubgraphForItem (SCH_ITEM *aItem)
 

Static Public Attributes

static bool m_allowRealTime = true
 

Private Member Functions

void updateItemConnectivity (const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
 Updates the graphical connectivity between items (i.e. More...
 
void buildConnectionGraph ()
 Generates the connection graph (after all item connectivity has been updated) More...
 
int assignNewNetCode (SCH_CONNECTION &aConnection)
 Helper to assign a new net code to a connection. More...
 
void assignNetCodesToBus (SCH_CONNECTION *aConnection)
 Ensures all members of the bus connection have a valid net code assigned. More...
 
void propagateToNeighbors (CONNECTION_SUBGRAPH *aSubgraph)
 Updates all neighbors of a subgraph with this one's connectivity info. More...
 
std::shared_ptr< SCH_CONNECTIONgetDefaultConnection (SCH_ITEM *aItem, CONNECTION_SUBGRAPH *aSubgraph)
 Builds a new default connection for the given item based on its properties. More...
 
void recacheSubgraphName (CONNECTION_SUBGRAPH *aSubgraph, const wxString &aOldName)
 
bool ercCheckBusToNetConflicts (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for conflicting connections between net and bus labels. More...
 
bool ercCheckBusToBusConflicts (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for conflicting connections between two bus items. More...
 
bool ercCheckBusToBusEntryConflicts (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for conflicting bus entry to bus connections. More...
 
bool ercCheckNoConnects (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for proper presence or absence of no-connect symbols. More...
 
bool ercCheckFloatingWires (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for floating wires. More...
 
bool ercCheckLabels (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for proper connection of labels. More...
 
int ercCheckHierSheets ()
 Checks that a hierarchical sheet has at least one matching label inside the sheet for each port on the parent sheet object. More...
 

Static Private Member Functions

static SCH_CONNECTIONmatchBusMember (SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
 Search for a matching bus member inside a bus connection. More...
 

Private Attributes

SCH_SHEET_LIST m_sheetList
 
std::vector< SCH_ITEM * > m_items
 
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
 
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
 
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
 
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
 
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
 
std::map< wxString, int > m_net_name_to_code_map
 
std::map< wxString, int > m_bus_name_to_code_map
 
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
 
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
 
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
 
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map
 
NET_MAP m_net_code_to_subgraphs_map
 
int m_last_net_code
 
int m_last_bus_code
 
int m_last_subgraph_code
 
SCHEMATICm_schematic
 The schematic this graph represents. More...
 

Detailed Description

Calculates the connectivity of a schematic and generates netlists.

Definition at line 223 of file connection_graph.h.

Constructor & Destructor Documentation

◆ CONNECTION_GRAPH()

CONNECTION_GRAPH::CONNECTION_GRAPH ( SCHEMATIC aSchematic = nullptr)
inline

Definition at line 226 of file connection_graph.h.

226  :
227  m_last_net_code( 1 ),
228  m_last_bus_code( 1 ),
230  m_schematic( aSchematic )
231  {}
SCHEMATIC * m_schematic
The schematic this graph represents.

◆ ~CONNECTION_GRAPH()

CONNECTION_GRAPH::~CONNECTION_GRAPH ( )
inline

Definition at line 233 of file connection_graph.h.

234  {
235  Reset();
236  }

References Reset().

Member Function Documentation

◆ assignNetCodesToBus()

void CONNECTION_GRAPH::assignNetCodesToBus ( SCH_CONNECTION aConnection)
private

Ensures all members of the bus connection have a valid net code assigned.

Parameters
aConnectionis a bus connection

Definition at line 1524 of file connection_graph.cpp.

1525 {
1526  auto connections_to_check( aConnection->Members() );
1527 
1528  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1529  {
1530  auto member = connections_to_check[i];
1531 
1532  if( member->IsBus() )
1533  {
1534  connections_to_check.insert( connections_to_check.end(),
1535  member->Members().begin(),
1536  member->Members().end() );
1537  continue;
1538  }
1539 
1540  assignNewNetCode( *member );
1541  }
1542 }
int assignNewNetCode(SCH_CONNECTION &aConnection)
Helper to assign a new net code to a connection.
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()

References assignNewNetCode(), and SCH_CONNECTION::Members().

Referenced by buildConnectionGraph().

◆ assignNewNetCode()

int CONNECTION_GRAPH::assignNewNetCode ( SCH_CONNECTION aConnection)
private

Helper to assign a new net code to a connection.

Returns
the assigned code

Definition at line 1504 of file connection_graph.cpp.

1505 {
1506  int code;
1507 
1508  if( m_net_name_to_code_map.count( aConnection.Name() ) )
1509  {
1510  code = m_net_name_to_code_map.at( aConnection.Name() );
1511  }
1512  else
1513  {
1514  code = m_last_net_code++;
1515  m_net_name_to_code_map[ aConnection.Name() ] = code;
1516  }
1517 
1518  aConnection.SetNetCode( code );
1519 
1520  return code;
1521 }
wxString Name(bool aIgnoreSheet=false) const
std::map< wxString, int > m_net_name_to_code_map
void SetNetCode(int aCode)

References m_last_net_code, m_net_name_to_code_map, SCH_CONNECTION::Name(), and SCH_CONNECTION::SetNetCode().

Referenced by assignNetCodesToBus(), and buildConnectionGraph().

◆ buildConnectionGraph()

void CONNECTION_GRAPH::buildConnectionGraph ( )
private

Generates the connection graph (after all item connectivity has been updated)

In the first phase, the algorithm iterates over all items, and then over all items that are connected (graphically) to each item, placing them into CONNECTION_SUBGRAPHs. Items that can potentially drive connectivity (i.e. labels, pins, etc.) are added to the m_drivers vector of the subgraph.

In the second phase, each subgraph is resolved. To resolve a subgraph, the driver is first selected by CONNECTION_SUBGRAPH::ResolveDrivers(), and then the connection for the chosen driver is propagated to all the other items in the subgraph.

TODO(JE)

It would be good if net codes were preserved as much as possible when generating netlists, so that unnamed nets don't keep shifting around when you regenerate.

Right now, we are clearing out the old connections up in UpdateItemConnectivity(), but that is useful information, so maybe we need to just set the dirty flag or something.

That way, ResolveDrivers() can check what the driver of the subgraph was previously, and if it is in the situation of choosing between equal candidates for an auto-generated net name, pick the previous one.

N.B. the old algorithm solves this by sorting the possible net names alphabetically, so as long as the same refdes components are involved, the net will be the same.

Definition at line 660 of file connection_graph.cpp.

661 {
662  // Recache all bus aliases for later use
663  wxCHECK_RET( m_schematic, "Connection graph cannot be built without schematic pointer" );
664 
665  SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
666 
667  for( unsigned i = 0; i < all_sheets.size(); i++ )
668  {
669  for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() )
670  m_bus_alias_cache[ alias->GetName() ] = alias;
671  }
672 
673  // Build subgraphs from items (on a per-sheet basis)
674 
675  for( SCH_ITEM* item : m_items )
676  {
677  for( const auto& it : item->m_connection_map )
678  {
679  const auto sheet = it.first;
680  auto connection = it.second;
681 
682  if( connection->SubgraphCode() == 0 )
683  {
684  auto subgraph = new CONNECTION_SUBGRAPH( this );
685 
686  subgraph->m_code = m_last_subgraph_code++;
687  subgraph->m_sheet = sheet;
688 
689  subgraph->AddItem( item );
690 
691  connection->SetSubgraphCode( subgraph->m_code );
692  m_item_to_subgraph_map[item] = subgraph;
693 
694  std::list<SCH_ITEM*> members;
695 
696  auto get_items =
697  [&]( SCH_ITEM* aItem ) -> bool
698  {
699  auto* conn = aItem->Connection( sheet );
700 
701  if( !conn )
702  conn = aItem->InitializeConnection( sheet, this );
703 
704  return ( conn->SubgraphCode() == 0 );
705  };
706 
707  std::copy_if( item->ConnectedItems( sheet ).begin(),
708  item->ConnectedItems( sheet ).end(),
709  std::back_inserter( members ), get_items );
710 
711  for( auto connected_item : members )
712  {
713  if( connected_item->Type() == SCH_NO_CONNECT_T )
714  subgraph->m_no_connect = connected_item;
715 
716  auto connected_conn = connected_item->Connection( sheet );
717 
718  wxASSERT( connected_conn );
719 
720  if( connected_conn->SubgraphCode() == 0 )
721  {
722  connected_conn->SetSubgraphCode( subgraph->m_code );
723  m_item_to_subgraph_map[connected_item] = subgraph;
724  subgraph->AddItem( connected_item );
725 
726  std::copy_if( connected_item->ConnectedItems( sheet ).begin(),
727  connected_item->ConnectedItems( sheet ).end(),
728  std::back_inserter( members ), get_items );
729  }
730  }
731 
732  subgraph->m_dirty = true;
733  m_subgraphs.push_back( subgraph );
734  }
735  }
736  }
737 
758  // Resolve drivers for subgraphs and propagate connectivity info
759 
760  // We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
761  size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
762  ( m_subgraphs.size() + 3 ) / 4 );
763 
764  std::atomic<size_t> nextSubgraph( 0 );
765  std::vector<std::future<size_t>> returns( parallelThreadCount );
766  std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
767 
768  std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
769  [&] ( const CONNECTION_SUBGRAPH* candidate )
770  {
771  return candidate->m_dirty;
772  } );
773 
774  auto update_lambda = [&nextSubgraph, &dirty_graphs]() -> size_t
775  {
776  for( size_t subgraphId = nextSubgraph++; subgraphId < dirty_graphs.size(); subgraphId = nextSubgraph++ )
777  {
778  auto subgraph = dirty_graphs[subgraphId];
779 
780  if( !subgraph->m_dirty )
781  continue;
782 
783  // Special processing for some items
784  for( auto item : subgraph->m_items )
785  {
786  switch( item->Type() )
787  {
788  case SCH_NO_CONNECT_T:
789  subgraph->m_no_connect = item;
790  break;
791 
793  subgraph->m_bus_entry = item;
794  break;
795 
796  case SCH_PIN_T:
797  {
798  auto pin = static_cast<SCH_PIN*>( item );
799 
800  if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
801  subgraph->m_no_connect = item;
802 
803  break;
804  }
805 
806  default:
807  break;
808  }
809  }
810 
811  if( !subgraph->ResolveDrivers() )
812  {
813  subgraph->m_dirty = false;
814  }
815  else
816  {
817  // Now the subgraph has only one driver
818  SCH_ITEM* driver = subgraph->m_driver;
819  SCH_SHEET_PATH sheet = subgraph->m_sheet;
820  SCH_CONNECTION* connection = driver->Connection( sheet );
821 
822  connection->ConfigureFromLabel( subgraph->GetNameForDriver( driver ) );
823  connection->SetDriver( driver );
824  connection->ClearDirty();
825 
826  subgraph->m_dirty = false;
827  }
828  }
829 
830  return 1;
831  };
832 
833  if( parallelThreadCount == 1 )
834  update_lambda();
835  else
836  {
837  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
838  returns[ii] = std::async( std::launch::async, update_lambda );
839 
840  // Finalize the threads
841  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
842  returns[ii].wait();
843  }
844 
845  // Now discard any non-driven subgraphs from further consideration
846 
847  std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
848  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
849  {
850  return candidate->m_driver;
851  } );
852 
853  // Check for subgraphs with the same net name but only weak drivers.
854  // For example, two wires that are both connected to hierarchical
855  // sheet pins that happen to have the same name, but are not the same.
856 
857  for( auto&& subgraph : m_driver_subgraphs )
858  {
859  wxString full_name = subgraph->m_driver_connection->Name();
860  wxString name = subgraph->m_driver_connection->Name( true );
861  m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
862 
863  subgraph->m_dirty = true;
864 
865  if( subgraph->m_strong_driver )
866  {
867  SCH_ITEM* driver = subgraph->m_driver;
868  SCH_SHEET_PATH sheet = subgraph->m_sheet;
869 
870  switch( driver->Type() )
871  {
872  case SCH_LABEL_T:
873  case SCH_HIER_LABEL_T:
874  {
875  m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
876  break;
877  }
878  case SCH_GLOBAL_LABEL_T:
879  {
880  m_global_label_cache[name].push_back( subgraph );
881  break;
882  }
883  case SCH_PIN_T:
884  {
885  auto pin = static_cast<SCH_PIN*>( driver );
886  wxASSERT( pin->IsPowerConnection() );
887  m_global_label_cache[name].push_back( subgraph );
888  break;
889  }
890  default:
891  wxLogTrace( "CONN", "Unexpected strong driver %s",
893  break;
894  }
895  }
896  }
897 
898  // Generate subgraphs for invisible power pins. These will be merged with other subgraphs
899  // on the same sheet in the next loop.
900 
901  std::unordered_map<int, CONNECTION_SUBGRAPH*> invisible_pin_subgraphs;
902 
903  for( const auto& it : m_invisible_power_pins )
904  {
905  SCH_SHEET_PATH sheet = it.first;
906  SCH_PIN* pin = it.second;
907 
908  if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParent()->IsPower() )
909  {
910  // ERC will warn about this: user has wired up an invisible pin
911  continue;
912  }
913 
914  SCH_CONNECTION* connection = pin->Connection( sheet );
915 
916  if( !connection )
917  connection = pin->InitializeConnection( sheet, this );
918 
919  // If this pin already has a subgraph, don't need to process
920  if( connection->SubgraphCode() > 0 )
921  continue;
922 
923  connection->SetName( pin->GetName() );
924 
925  int code = assignNewNetCode( *connection );
926 
927  connection->SetNetCode( code );
928 
929  CONNECTION_SUBGRAPH* subgraph;
930 
931  if( invisible_pin_subgraphs.count( code ) )
932  {
933  subgraph = invisible_pin_subgraphs.at( code );
934  subgraph->AddItem( pin );
935  }
936  else
937  {
938  subgraph = new CONNECTION_SUBGRAPH( this );
939 
940  subgraph->m_code = m_last_subgraph_code++;
941  subgraph->m_sheet = sheet;
942 
943  subgraph->AddItem( pin );
944  subgraph->ResolveDrivers();
945 
946  auto key = std::make_pair( subgraph->GetNetName(), code );
947  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
948  m_subgraphs.push_back( subgraph );
949  m_driver_subgraphs.push_back( subgraph );
950 
951  invisible_pin_subgraphs[code] = subgraph;
952  }
953 
954  connection->SetSubgraphCode( subgraph->m_code );
955  }
956 
957  for( auto it : invisible_pin_subgraphs )
958  it.second->UpdateItemConnections();
959 
960  // Here we do all the local (sheet) processing of each subgraph, including assigning net
961  // codes, merging subgraphs together that use label connections, etc.
962 
963  // Cache remaining valid subgraphs by sheet path
964  for( auto subgraph : m_driver_subgraphs )
965  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
966 
967  std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
968 
969  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
970  {
971  if( subgraph->m_absorbed )
972  continue;
973 
974  SCH_CONNECTION* connection = subgraph->m_driver_connection;
975  SCH_SHEET_PATH sheet = subgraph->m_sheet;
976  wxString name = connection->Name();
977 
978  // Test subgraphs with weak drivers for net name conflicts and fix them
979  unsigned suffix = 1;
980 
981  auto create_new_name =
982  [&suffix]( SCH_CONNECTION* aConn ) -> wxString
983  {
984  wxString newName;
985 
986  // For group buses with a prefix, we can add the suffix to the prefix.
987  // If they don't have a prefix, we force the creation of a prefix so that
988  // two buses don't get inadvertently shorted together.
989  if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
990  {
991  wxString prefix = aConn->BusPrefix();
992 
993  if( prefix.empty() )
994  prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
995 
996  wxString oldName = aConn->Name().AfterFirst( '{' );
997 
998  newName = wxString::Format( "%s_%u{%s", prefix, suffix, oldName );
999 
1000  aConn->ConfigureFromLabel( newName );
1001  }
1002  else
1003  {
1004  newName = wxString::Format( "%s_%u", aConn->Name(), suffix );
1005  aConn->SetSuffix( wxString::Format( "_%u", suffix ) );
1006  }
1007 
1008  suffix++;
1009  return newName;
1010  };
1011 
1012  if( !subgraph->m_strong_driver )
1013  {
1014  auto& vec = m_net_name_to_subgraphs_map.at( name );
1015 
1016  if( vec.size() > 1 )
1017  {
1018  wxString new_name = create_new_name( connection );
1019 
1020  while( m_net_name_to_subgraphs_map.count( new_name ) )
1021  new_name = create_new_name( connection );
1022 
1023  wxLogTrace( "CONN", "%ld (%s) is weakly driven and not unique. Changing to %s.",
1024  subgraph->m_code, name, new_name );
1025 
1026  vec.erase( std::remove( vec.begin(), vec.end(), subgraph ), vec.end() );
1027 
1028  m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1029 
1030  name = new_name;
1031 
1032  subgraph->UpdateItemConnections();
1033  }
1034  else
1035  {
1036  // If there is no conflict, promote sheet pins to be strong drivers so that they
1037  // will be considered below for propagation/merging.
1038 
1039  // It is possible for this to generate a conflict if the sheet pin has the same
1040  // name as a global label on the same sheet, because global merging will then treat
1041  // this subgraph as if it had a matching local label. So, for those cases, we
1042  // don't apply this promotion
1043 
1044  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1045  {
1046  bool conflict = false;
1047  wxString global_name = connection->Name( true );
1048  size_t count = m_net_name_to_subgraphs_map.count( global_name );
1049 
1050  if( count )
1051  {
1052  // A global will conflict if it is on the same sheet as this subgraph, since
1053  // it would be connected by implicit local label linking
1054  auto& candidates = m_net_name_to_subgraphs_map.at( global_name );
1055 
1056  for( const auto& candidate : candidates )
1057  {
1058  if( candidate->m_sheet == sheet )
1059  conflict = true;
1060  }
1061  }
1062 
1063  if( conflict )
1064  {
1065  wxLogTrace( "CONN",
1066  "%ld (%s) skipped for promotion due to potential conflict",
1067  subgraph->m_code, name );
1068  }
1069  else
1070  {
1071  wxLogTrace( "CONN",
1072  "%ld (%s) weakly driven by unique sheet pin %s, promoting",
1073  subgraph->m_code, name,
1075 
1076  subgraph->m_strong_driver = true;
1077  }
1078  }
1079  }
1080  }
1081 
1082  // Assign net codes
1083 
1084  if( connection->IsBus() )
1085  {
1086  int code = -1;
1087 
1088  if( m_bus_name_to_code_map.count( name ) )
1089  {
1090  code = m_bus_name_to_code_map.at( name );
1091  }
1092  else
1093  {
1094  code = m_last_bus_code++;
1095  m_bus_name_to_code_map[ name ] = code;
1096  }
1097 
1098  connection->SetBusCode( code );
1099  assignNetCodesToBus( connection );
1100  }
1101  else
1102  {
1103  assignNewNetCode( *connection );
1104  }
1105 
1106  subgraph->UpdateItemConnections();
1107 
1108  // Reset the flag for the next loop below
1109  subgraph->m_dirty = true;
1110 
1111  // Next, we merge together subgraphs that have label connections, and create
1112  // neighbor links for subgraphs that are part of a bus on the same sheet.
1113  // For merging, we consider each possible strong driver.
1114 
1115  // If this subgraph doesn't have a strong driver, let's skip it, since there is no
1116  // way it will be merged with anything.
1117 
1118  if( !subgraph->m_strong_driver )
1119  continue;
1120 
1121  // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
1122  // as the subgraph we are considering that has a strong driver.
1123  // Weakly driven subgraphs are not considered since they will never be absorbed or
1124  // form neighbor links.
1125 
1126  std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
1127  std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
1128  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
1129  std::back_inserter( candidate_subgraphs ),
1130  [&] ( const CONNECTION_SUBGRAPH* candidate )
1131  {
1132  return ( !candidate->m_absorbed &&
1133  candidate->m_strong_driver &&
1134  candidate != subgraph );
1135  } );
1136 
1137  // This is a list of connections on the current subgraph to compare to the
1138  // drivers of each candidate subgraph. If the current subgraph is a bus,
1139  // we should consider each bus member.
1140  std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
1141 
1142  // Also check the main driving connection
1143  connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
1144 
1145  auto add_connections_to_check = [&] ( CONNECTION_SUBGRAPH* aSubgraph ) {
1146  for( SCH_ITEM* possible_driver : aSubgraph->m_items )
1147  {
1148  if( possible_driver == aSubgraph->m_driver )
1149  continue;
1150 
1151  auto c = getDefaultConnection( possible_driver, aSubgraph );
1152 
1153  if( c )
1154  {
1155  if( c->Type() != aSubgraph->m_driver_connection->Type() )
1156  continue;
1157 
1158  if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
1159  continue;
1160 
1161  connections_to_check.push_back( c );
1162  wxLogTrace( "CONN", "%lu (%s): Adding secondary driver %s", aSubgraph->m_code,
1163  aSubgraph->m_driver_connection->Name( true ), c->Name( true ) );
1164  }
1165  }
1166  };
1167 
1168  // Now add other strong drivers
1169  // The actual connection attached to these items will have been overwritten
1170  // by the chosen driver of the subgraph, so we need to create a dummy connection
1171  add_connections_to_check( subgraph );
1172 
1173  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1174  {
1175  auto member = connections_to_check[i];
1176 
1177  if( member->IsBus() )
1178  {
1179  connections_to_check.insert( connections_to_check.end(),
1180  member->Members().begin(),
1181  member->Members().end() );
1182  }
1183 
1184  wxString test_name = member->Name( true );
1185 
1186  for( auto candidate : candidate_subgraphs )
1187  {
1188  if( candidate->m_absorbed )
1189  continue;
1190 
1191  bool match = false;
1192 
1193  if( candidate->m_driver_connection->Name( true ) == test_name )
1194  {
1195  match = true;
1196  }
1197  else
1198  {
1199  if( !candidate->m_multiple_drivers )
1200  continue;
1201 
1202  for( SCH_ITEM *driver : candidate->m_drivers )
1203  {
1204  if( driver == candidate->m_driver )
1205  continue;
1206 
1207  // Sheet pins are not candidates for merging
1208  if( driver->Type() == SCH_SHEET_PIN_T )
1209  continue;
1210 
1211  if( driver->Type() == SCH_PIN_T )
1212  {
1213  auto pin = static_cast<SCH_PIN*>( driver );
1214 
1215  if( pin->IsPowerConnection() && pin->GetName() == test_name )
1216  {
1217  match = true;
1218  break;
1219  }
1220  }
1221  else
1222  {
1223  wxASSERT( driver->Type() == SCH_LABEL_T ||
1224  driver->Type() == SCH_GLOBAL_LABEL_T ||
1225  driver->Type() == SCH_HIER_LABEL_T );
1226 
1227  if( subgraph->GetNameForDriver( driver ) == test_name )
1228  {
1229  match = true;
1230  break;
1231  }
1232  }
1233  }
1234  }
1235 
1236  if( match )
1237  {
1238  if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
1239  {
1240  wxLogTrace( "CONN", "%lu (%s) has bus child %lu (%s)", subgraph->m_code,
1241  connection->Name(), candidate->m_code, member->Name() );
1242 
1243  subgraph->m_bus_neighbors[member].insert( candidate );
1244  candidate->m_bus_parents[member].insert( subgraph );
1245  }
1246  else
1247  {
1248  wxLogTrace( "CONN", "%lu (%s) absorbs neighbor %lu (%s)",
1249  subgraph->m_code, connection->Name(),
1250  candidate->m_code, candidate->m_driver_connection->Name() );
1251 
1252  // Candidate may have other non-chosen drivers we need to follow
1253  add_connections_to_check( candidate );
1254 
1255  subgraph->Absorb( candidate );
1256  invalidated_subgraphs.insert( subgraph );
1257  }
1258  }
1259  }
1260  }
1261  }
1262 
1263  // Update any subgraph that was invalidated above
1264  for( auto subgraph : invalidated_subgraphs )
1265  {
1266  if( subgraph->m_absorbed )
1267  continue;
1268 
1269  subgraph->ResolveDrivers();
1270 
1271  if( subgraph->m_driver_connection->IsBus() )
1273  else
1274  assignNewNetCode( *subgraph->m_driver_connection );
1275 
1276  subgraph->UpdateItemConnections();
1277 
1278  wxLogTrace( "CONN", "Re-resolving drivers for %lu (%s)", subgraph->m_code,
1279  subgraph->m_driver_connection->Name() );
1280  }
1281 
1282  // Absorbed subgraphs should no longer be considered
1283  m_driver_subgraphs.erase( std::remove_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1284  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1285  {
1286  return candidate->m_absorbed;
1287  } ),
1288  m_driver_subgraphs.end() );
1289 
1290  // Store global subgraphs for later reference
1291  std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
1292  std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1293  std::back_inserter( global_subgraphs ),
1294  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1295  {
1296  return !candidate->m_local_driver;
1297  } );
1298 
1299  // Recache remaining valid subgraphs by sheet path
1300  m_sheet_to_subgraphs_map.clear();
1301  for( auto subgraph : m_driver_subgraphs )
1302  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1303 
1304  // Next time through the subgraphs, we do some post-processing to handle things like
1305  // connecting bus members to their neighboring subgraphs, and then propagate connections
1306  // through the hierarchy
1307 
1308  for( auto subgraph : m_driver_subgraphs )
1309  {
1310  if( !subgraph->m_dirty )
1311  continue;
1312 
1313  // For subgraphs that are driven by a global (power port or label) and have more
1314  // than one global driver, we need to seek out other subgraphs driven by the
1315  // same name as the non-chosen driver and update them to match the chosen one.
1316 
1317  if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
1318  {
1319  for( SCH_ITEM* driver : subgraph->m_drivers )
1320  {
1321  if( driver == subgraph->m_driver )
1322  continue;
1323 
1324  wxString secondary_name = subgraph->GetNameForDriver( driver );
1325 
1326  if( secondary_name == subgraph->m_driver_connection->Name() )
1327  continue;
1328 
1329  bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
1331 
1332  for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
1333  {
1334  if( candidate == subgraph )
1335  continue;
1336 
1337  if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
1338  continue;
1339 
1340  SCH_CONNECTION* conn = candidate->m_driver_connection;
1341 
1342  if( conn->Name() == secondary_name )
1343  {
1344  wxLogTrace( "CONN", "Global %lu (%s) promoted to %s", candidate->m_code,
1345  conn->Name(), subgraph->m_driver_connection->Name() );
1346 
1347  conn->Clone( *subgraph->m_driver_connection );
1348  candidate->UpdateItemConnections();
1349 
1350  candidate->m_dirty = false;
1351  }
1352  }
1353  }
1354  }
1355 
1356  // This call will handle descending the hierarchy and updating child subgraphs
1357  propagateToNeighbors( subgraph );
1358  }
1359 
1360  // Handle buses that have been linked together somewhere by member (net) connections.
1361  // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
1362 
1363  // For net subgraphs that have more than one bus parent, we need to ensure that those
1364  // buses are linked together in the final netlist. The final name of each bus might not
1365  // match the local name that was used to establish the parent-child relationship, because
1366  // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
1367  // we need to identify the appropriate bus members to link together (and their final names),
1368  // and then update all instances of the old name in the hierarchy.
1369 
1370  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1371  {
1372  if( subgraph->m_bus_parents.size() < 2 )
1373  continue;
1374 
1375  SCH_CONNECTION* conn = subgraph->m_driver_connection;
1376 
1377  wxLogTrace( "CONN", "%lu (%s) has multiple bus parents", subgraph->m_code, conn->Name() );
1378 
1379  wxASSERT( conn->IsNet() );
1380 
1381  for( const auto& it : subgraph->m_bus_parents )
1382  {
1383  SCH_CONNECTION* link_member = it.first.get();
1384 
1385  for( CONNECTION_SUBGRAPH* parent : it.second )
1386  {
1387  while( parent->m_absorbed )
1388  parent = parent->m_absorbed_by;
1389 
1390  SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
1391 
1392  if( !match )
1393  {
1394  wxLogTrace( "CONN", "Warning: could not match %s inside %lu (%s)", conn->Name(),
1395  parent->m_code, parent->m_driver_connection->Name() );
1396  continue;
1397  }
1398 
1399  if( conn->Name() != match->Name() )
1400  {
1401  wxString old_name = match->Name();
1402 
1403  wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", parent->m_code,
1404  parent->m_driver_connection->Name(), old_name, conn->Name() );
1405 
1406  match->Clone( *conn );
1407 
1408  if( !m_net_name_to_subgraphs_map.count( old_name ) )
1409  continue;
1410 
1411  for( CONNECTION_SUBGRAPH* old_sg : m_net_name_to_subgraphs_map.at( old_name ) )
1412  {
1413  while( old_sg->m_absorbed )
1414  old_sg = old_sg->m_absorbed_by;
1415 
1416  old_sg->m_driver_connection->Clone( *conn );
1417  old_sg->UpdateItemConnections();
1418  }
1419  }
1420  }
1421  }
1422  }
1423 
1426 
1427  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1428  {
1429  // Every driven subgraph should have been marked by now
1430  if( subgraph->m_dirty )
1431  {
1432  // TODO(JE) this should be caught by hierarchical sheet port/pin ERC, check this
1433  // Reset to false so no complaints come up later
1434  subgraph->m_dirty = false;
1435  }
1436 
1437  if( subgraph->m_driver_connection->IsBus() )
1438  {
1439  // No other processing to do on buses
1440  continue;
1441  }
1442  else
1443  {
1444  // As a visual aid, we can check sheet pins that are driven by themselves to see
1445  // if they should be promoted to buses
1446 
1447  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1448  {
1449  SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
1450 
1451  if( SCH_SHEET* sheet = pin->GetParent() )
1452  {
1453  wxString pinText = pin->GetText();
1454 
1455  for( auto item : sheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
1456  {
1457  auto label = static_cast<SCH_HIERLABEL*>( item );
1458 
1459  if( label->GetText() == pinText )
1460  {
1461  SCH_SHEET_PATH path = subgraph->m_sheet;
1462  path.push_back( sheet );
1463 
1464  SCH_CONNECTION* parent_conn = label->Connection( path );
1465 
1466  if( parent_conn && parent_conn->IsBus() )
1468 
1469  break;
1470  }
1471  }
1472 
1473  if( subgraph->m_driver_connection->IsBus() )
1474  continue;
1475  }
1476  }
1477  }
1478 
1479  auto key = std::make_pair( subgraph->GetNetName(),
1480  subgraph->m_driver_connection->NetCode() );
1481  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1482 
1483  m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
1484  }
1485 
1486  // Clean up and deallocate stale subgraphs
1487  m_subgraphs.erase( std::remove_if( m_subgraphs.begin(), m_subgraphs.end(),
1488  [&]( const CONNECTION_SUBGRAPH* sg )
1489  {
1490  if( sg->m_absorbed )
1491  {
1492  delete sg;
1493  return true;
1494  }
1495  else
1496  {
1497  return false;
1498  }
1499  } ),
1500  m_subgraphs.end() );
1501 }
SCH_SHEET_PATH m_sheet
SCH_SHEET_LIST.
void SetBusCode(int aCode)
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
LIB_PIN * GetLibPin() const
Definition: sch_pin.h:67
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph)
Updates all neighbors of a subgraph with this one's connectivity info.
SCH_SHEET * GetParent() const
Get the parent sheet object of this sheet pin.
Definition: sch_sheet.h:168
SCH_SHEET_LIST GetSheets() const
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:89
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
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...
LIB_PART * GetParent() const
Definition: lib_item.h:182
void Absorb(CONNECTION_SUBGRAPH *aOther)
Combines another subgraph on the same sheet into this one.
This item represents a bus group.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
bool m_local_driver
True if the driver is a local (i.e. non-global) type.
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
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.
void ConfigureFromLabel(const wxString &aLabel)
Configures the connection given a label.
void SetDriver(SCH_ITEM *aItem)
void SetName(const wxString &aName)
NET_MAP m_net_code_to_subgraphs_map
std::vector< SCH_ITEM * > m_items
SCHEMATIC * m_schematic
The schematic this graph represents.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
A subgraph is a set of items that are electrically connected on a single sheet.
bool ResolveDrivers(bool aCreateMarkers=false)
Determines which potential driver should drive the subgraph.
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
int NetCode() const
SCH_CONNECTION * InitializeConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Creates a new connection object associated with this object.
Definition: sch_item.cpp:190
wxString Name(bool aIgnoreSheet=false) const
const wxString & GetNameForDriver(SCH_ITEM *aItem)
Returns the candidate net name for a driver.
int assignNewNetCode(SCH_CONNECTION &aConnection)
Helper to assign a new net code to a connection.
void SetSubgraphCode(int aCode)
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
void AddItem(SCH_ITEM *aItem)
Adds a new item to the subgraph.
int SubgraphCode() const
bool m_strong_driver
True if the driver is "strong": a label or power object.
std::vector< SCH_ITEM * > m_drivers
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
SCH_SHEET_PATH.
void Clone(SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
void assignNetCodesToBus(SCH_CONNECTION *aConnection)
Ensures all members of the bus connection have a valid net code assigned.
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:150
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:216
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
bool IsNet() const
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
bool IsPower() const
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
SCH_ITEM_SET & ConnectedItems(const SCH_SHEET_PATH &aPath)
Retrieves the set of items connected to this item on the given sheet.
Definition: sch_item.cpp:178
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
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.
virtual wxString GetSelectMenuText(EDA_UNITS aUnits) const
Function GetSelectMenuText returns the text to display to be used in the selection clarification cont...
std::map< wxString, int > m_bus_name_to_code_map
bool IsPowerConnection() const
Definition: sch_pin.h:123
void SetType(CONNECTION_TYPE aType)
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
not connected (must be left open)
bool IsBus() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:127
void SetNetCode(int aCode)
wxString GetNetName() const
Returns the fully-qualified net name for this subgraph (if one exists)
wxString GetName() const
Definition: sch_pin.cpp:65
This item represents a bus vector.
static SCH_CONNECTION * matchBusMember(SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
Search for a matching bus member inside a bus connection.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
void UpdateItemConnections()
Updates all items to match the driver connection.

References CONNECTION_SUBGRAPH::AddItem(), assignNetCodesToBus(), assignNewNetCode(), BUS, BUS_GROUP, SCH_CONNECTION::ClearDirty(), SCH_CONNECTION::Clone(), SCH_CONNECTION::ConfigureFromLabel(), SCH_ITEM::ConnectedItems(), SCH_ITEM::Connection(), Format(), getDefaultConnection(), CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_PIN::GetLibPin(), SCH_PIN::GetName(), CONNECTION_SUBGRAPH::GetNetName(), SCH_SHEET_PIN::GetParent(), LIB_ITEM::GetParent(), EDA_ITEM::GetSelectMenuText(), SCHEMATIC::GetSheets(), EDA_TEXT::GetText(), SCH_ITEM::InitializeConnection(), SCH_CONNECTION::IsBus(), SCH_CONNECTION::IsNet(), LIB_PART::IsPower(), CONNECTION_SUBGRAPH::m_absorbed, CONNECTION_SUBGRAPH::m_absorbed_by, m_bus_alias_cache, m_bus_name_to_code_map, CONNECTION_SUBGRAPH::m_code, CONNECTION_SUBGRAPH::m_driver_connection, m_driver_subgraphs, m_global_label_cache, m_invisible_power_pins, m_item_to_subgraph_map, m_items, m_last_bus_code, m_last_subgraph_code, m_local_label_cache, m_net_code_to_subgraphs_map, m_net_name_to_subgraphs_map, m_schematic, CONNECTION_SUBGRAPH::m_sheet, m_sheet_to_subgraphs_map, m_subgraphs, matchBusMember(), MILLIMETRES, name, SCH_CONNECTION::Name(), CONNECTION_SUBGRAPH::POWER_PIN, propagateToNeighbors(), PT_NC, SCH_SHEET_PATH::push_back(), CONNECTION_SUBGRAPH::ResolveDrivers(), SCH_BUS_WIRE_ENTRY_T, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T, SCH_NO_CONNECT_T, SCH_PIN_T, SCH_SHEET_PIN_T, SCH_CONNECTION::SetBusCode(), SCH_CONNECTION::SetDriver(), SCH_CONNECTION::SetName(), SCH_CONNECTION::SetNetCode(), SCH_CONNECTION::SetSubgraphCode(), SCH_CONNECTION::SetType(), SCH_CONNECTION::SubgraphCode(), and EDA_ITEM::Type().

Referenced by Recalculate().

◆ ercCheckBusToBusConflicts()

bool CONNECTION_GRAPH::ercCheckBusToBusConflicts ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for conflicting connections between two bus items.

For example, a labeled bus wire connected to a hierarchical sheet pin where the labeled bus doesn't contain any of the same bus members as the sheet pin

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2143 of file connection_graph.cpp.

2144 {
2145  wxString msg;
2146  auto sheet = aSubgraph->m_sheet;
2147  auto screen = sheet.LastScreen();
2148 
2149  SCH_ITEM* label = nullptr;
2150  SCH_ITEM* port = nullptr;
2151 
2152  for( auto item : aSubgraph->m_items )
2153  {
2154  switch( item->Type() )
2155  {
2156  case SCH_TEXT_T:
2157  case SCH_GLOBAL_LABEL_T:
2158  {
2159  if( !label && item->Connection( sheet )->IsBus() )
2160  label = item;
2161  break;
2162  }
2163 
2164  case SCH_SHEET_PIN_T:
2165  case SCH_HIER_LABEL_T:
2166  {
2167  if( !port && item->Connection( sheet )->IsBus() )
2168  port = item;
2169  break;
2170  }
2171 
2172  default:
2173  break;
2174  }
2175  }
2176 
2177  if( label && port )
2178  {
2179  bool match = false;
2180 
2181  for( const auto& member : label->Connection( sheet )->Members() )
2182  {
2183  for( const auto& test : port->Connection( sheet )->Members() )
2184  {
2185  if( test != member && member->Name() == test->Name() )
2186  {
2187  match = true;
2188  break;
2189  }
2190  }
2191 
2192  if( match )
2193  break;
2194  }
2195 
2196  if( !match )
2197  {
2198  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
2199  ercItem->SetItems( label, port );
2200 
2201  SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
2202  screen->Append( marker );
2203 
2204  return false;
2205  }
2206  }
2207 
2208  return true;
2209 }
SCH_SHEET_PATH m_sheet
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
std::vector< SCH_ITEM * > m_items
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:150
SCH_SCREEN * LastScreen()
Function LastScreen.
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:194

References SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_BUS_TO_BUS_CONFLICT, EDA_ITEM::GetPosition(), SCH_CONNECTION::IsBus(), SCH_SHEET_PATH::LastScreen(), CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, SCH_CONNECTION::Members(), SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_SHEET_PIN_T, and SCH_TEXT_T.

Referenced by RunERC().

◆ ercCheckBusToBusEntryConflicts()

bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for conflicting bus entry to bus connections.

For example, a wire with label "A0" is connected to a bus labeled "D[8..0]"

Will also check for mistakes related to bus group names, for example: A bus group named "USB{DP DM}" should have bus entry connections like "USB.DP" but someone might accidentally just enter "DP"

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2212 of file connection_graph.cpp.

2213 {
2214  bool conflict = false;
2215  auto sheet = aSubgraph->m_sheet;
2216  auto screen = sheet.LastScreen();
2217 
2218  SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2219  SCH_ITEM* bus_wire = nullptr;
2220 
2221  for( auto item : aSubgraph->m_items )
2222  {
2223  switch( item->Type() )
2224  {
2225  case SCH_BUS_WIRE_ENTRY_T:
2226  {
2227  if( !bus_entry )
2228  bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2229  break;
2230  }
2231 
2232  default:
2233  break;
2234  }
2235  }
2236 
2237  if( bus_entry && bus_entry->m_connected_bus_item )
2238  {
2239  bus_wire = bus_entry->m_connected_bus_item;
2240 
2241  wxASSERT( bus_wire->Type() == SCH_LINE_T );
2242 
2243  // In some cases, the connection list (SCH_CONNECTION*) can be null.
2244  // Skip null connections.
2245  if( bus_entry->Connection( sheet ) && bus_wire->Type() == SCH_LINE_T
2246  && bus_wire->Connection( sheet ) )
2247  {
2248  conflict = true;
2249 
2250  auto test_name = bus_entry->Connection( sheet )->Name( true );
2251 
2252  for( const auto& member : bus_wire->Connection( sheet )->Members() )
2253  {
2254  if( member->Type() == CONNECTION_TYPE::BUS )
2255  {
2256  for( const auto& sub_member : member->Members() )
2257  {
2258  if( sub_member->Name( true ) == test_name )
2259  conflict = false;
2260  }
2261  }
2262  else if( member->Name( true ) == test_name )
2263  {
2264  conflict = false;
2265  }
2266  }
2267  }
2268  }
2269 
2270  // Don't report warnings if this bus member has been overridden by a higher priority power pin
2271  // or global label
2272  if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
2274  conflict = false;
2275 
2276  if( conflict )
2277  {
2278  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
2279  ercItem->SetItems( bus_entry, bus_wire );
2280 
2281  SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
2282  screen->Append( marker );
2283 
2284  return false;
2285  }
2286 
2287  return true;
2288 }
SCH_SHEET_PATH m_sheet
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 ...
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
wxString Name(bool aIgnoreSheet=false) const
std::vector< SCH_ITEM * > m_items
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:150
SCH_SCREEN * LastScreen()
Function LastScreen.
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
Class for a wire to bus entry.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
This item represents a bus vector.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
wxPoint GetPosition() const override

References BUS, SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_BUS_ENTRY_CONFLICT, CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_BUS_ENTRY_BASE::GetPosition(), SCH_SHEET_PATH::LastScreen(), SCH_BUS_WIRE_ENTRY::m_connected_bus_item, CONNECTION_SUBGRAPH::m_driver, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, SCH_CONNECTION::Members(), SCH_CONNECTION::Name(), CONNECTION_SUBGRAPH::POWER_PIN, SCH_BUS_WIRE_ENTRY_T, SCH_LINE_T, and EDA_ITEM::Type().

Referenced by RunERC().

◆ ercCheckBusToNetConflicts()

bool CONNECTION_GRAPH::ercCheckBusToNetConflicts ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for conflicting connections between net and bus labels.

For example, a net wire connected to a bus port/pin, or vice versa

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2087 of file connection_graph.cpp.

2088 {
2089  auto sheet = aSubgraph->m_sheet;
2090  auto screen = sheet.LastScreen();
2091 
2092  SCH_ITEM* net_item = nullptr;
2093  SCH_ITEM* bus_item = nullptr;
2094  SCH_CONNECTION conn( this );
2095 
2096  for( auto item : aSubgraph->m_items )
2097  {
2098  switch( item->Type() )
2099  {
2100  case SCH_LINE_T:
2101  {
2102  if( item->GetLayer() == LAYER_BUS )
2103  bus_item = ( !bus_item ) ? item : bus_item;
2104  else
2105  net_item = ( !net_item ) ? item : net_item;
2106  break;
2107  }
2108 
2109  case SCH_GLOBAL_LABEL_T:
2110  case SCH_SHEET_PIN_T:
2111  case SCH_HIER_LABEL_T:
2112  {
2113  SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2114  conn.ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
2115 
2116  if( conn.IsBus() )
2117  bus_item = ( !bus_item ) ? item : bus_item;
2118  else
2119  net_item = ( !net_item ) ? item : net_item;
2120  break;
2121  }
2122 
2123  default:
2124  break;
2125  }
2126  }
2127 
2128  if( net_item && bus_item )
2129  {
2130  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
2131  ercItem->SetItems( net_item, bus_item );
2132 
2133  SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
2134  screen->Append( marker );
2135 
2136  return false;
2137  }
2138 
2139  return true;
2140 }
SCH_SHEET_PATH m_sheet
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
std::vector< SCH_ITEM * > m_items
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_text.cpp:483
SCH_SCREEN * LastScreen()
Function LastScreen.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
These Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which ar...
Definition: string.cpp:77
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194

References SCH_CONNECTION::ConfigureFromLabel(), ERC_ITEM::Create(), CTX_NETNAME, ERCE_BUS_TO_NET_CONFLICT, EscapeString(), EDA_ITEM::GetPosition(), SCH_TEXT::GetShownText(), SCH_CONNECTION::IsBus(), SCH_SHEET_PATH::LastScreen(), LAYER_BUS, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LINE_T, and SCH_SHEET_PIN_T.

Referenced by RunERC().

◆ ercCheckFloatingWires()

bool CONNECTION_GRAPH::ercCheckFloatingWires ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for floating wires.

Will throw an error for any subgraph that consists of just wires with no driver

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2446 of file connection_graph.cpp.

2447 {
2448  if( aSubgraph->m_driver )
2449  return true;
2450 
2451  std::vector<SCH_LINE*> wires;
2452 
2453  // We've gotten this far, so we know we have no valid driver. All we need to do is check
2454  // for a wire that we can place the error on.
2455 
2456  for( SCH_ITEM* item : aSubgraph->m_items )
2457  {
2458  if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
2459  wires.emplace_back( static_cast<SCH_LINE*>( item ) );
2460  }
2461 
2462  if( !wires.empty() )
2463  {
2464  SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
2465 
2466  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
2467  ercItem->SetItems( wires[0],
2468  wires.size() > 1 ? wires[1] : nullptr,
2469  wires.size() > 2 ? wires[2] : nullptr,
2470  wires.size() > 3 ? wires[3] : nullptr );
2471 
2472  SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
2473  screen->Append( marker );
2474 
2475  return false;
2476  }
2477 
2478  return true;
2479 }
SCH_SHEET_PATH m_sheet
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
std::vector< SCH_ITEM * > m_items
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:279
SCH_SCREEN * LastScreen()
Function LastScreen.
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:131
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193

References SCH_SCREEN::Append(), ERC_ITEM::Create(), ERCE_WIRE_DANGLING, SCH_ITEM::GetLayer(), SCH_SHEET_PATH::LastScreen(), LAYER_WIRE, CONNECTION_SUBGRAPH::m_driver, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, SCH_LINE_T, and EDA_ITEM::Type().

Referenced by RunERC().

◆ ercCheckHierSheets()

int CONNECTION_GRAPH::ercCheckHierSheets ( )
private

Checks that a hierarchical sheet has at least one matching label inside the sheet for each port on the parent sheet object.

Parameters
aSubgraphis the subgraph to examine
Returns
the number of errors found

Definition at line 2580 of file connection_graph.cpp.

2581 {
2582  int errors = 0;
2583 
2584  for( const SCH_SHEET_PATH& sheet : m_sheetList )
2585  {
2586  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
2587  {
2588  if( item->Type() != SCH_SHEET_T )
2589  continue;
2590 
2591  SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
2592 
2593  std::map<wxString, SCH_SHEET_PIN*> pins;
2594 
2595  for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
2596  pins[pin->GetText()] = pin;
2597 
2598  for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
2599  {
2600  if( subItem->Type() == SCH_HIER_LABEL_T )
2601  {
2602  SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
2603  pins.erase( label->GetText() );
2604  }
2605  }
2606 
2607  for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
2608  {
2609  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
2610  ercItem->SetItems( unmatched.second );
2611  ercItem->SetErrorMessage( wxString::Format(
2612  _( "Sheet port %s has no matching hierarchical label inside the sheet" ),
2613  unmatched.first ) );
2614 
2615  SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
2616  sheet.LastScreen()->Append( marker );
2617 
2618  errors++;
2619  }
2620  }
2621  }
2622 
2623  return errors;
2624 }
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:282
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
SCH_SHEET_PATH.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:216
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:362
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
#define _(s)
Definition: 3d_actions.cpp:33
SCH_SHEET_LIST m_sheetList
EE_RTREE & Items()
Definition: sch_screen.h:158
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:127

References _, ERC_ITEM::Create(), ERCE_HIERACHICAL_LABEL, Format(), SCH_SHEET::GetPins(), SCH_SHEET::GetScreen(), EDA_TEXT::GetText(), SCH_SCREEN::Items(), m_sheetList, SCH_HIER_LABEL_T, and SCH_SHEET_T.

Referenced by RunERC().

◆ ercCheckLabels()

bool CONNECTION_GRAPH::ercCheckLabels ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for proper connection of labels.

Labels should be connected to something

Parameters
aSubgraphis the subgraph to examine
aCheckGlobalLabelsis true if global labels should be checked for loneliness
Returns
true for no errors, false for errors

Definition at line 2482 of file connection_graph.cpp.

2483 {
2484  // Label connection rules:
2485  // Local labels are flagged if they don't connect to any pins and don't have a no-connect
2486  // Global labels are flagged if they appear only once, don't connect to any local labels,
2487  // and don't have a no-connect marker
2488 
2489  // So, if there is a no-connect, we will never generate a warning here
2490  if( aSubgraph->m_no_connect )
2491  return true;
2492 
2493  SCH_TEXT* text = nullptr;
2494  bool has_other_connections = false;
2495 
2496  for( auto item : aSubgraph->m_items )
2497  {
2498  switch( item->Type() )
2499  {
2500  case SCH_LABEL_T:
2501  case SCH_GLOBAL_LABEL_T:
2502  case SCH_HIER_LABEL_T:
2503  text = static_cast<SCH_TEXT*>( item );
2504  break;
2505 
2506  case SCH_PIN_T:
2507  case SCH_SHEET_PIN_T:
2508  has_other_connections = true;
2509  break;
2510 
2511  default:
2512  break;
2513  }
2514  }
2515 
2516  if( !text )
2517  return true;
2518 
2519  bool isGlobal = text->Type() == SCH_GLOBAL_LABEL_T;
2520 
2521  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2522 
2523  // Global label check can be disabled independently
2524  if( !m_schematic->ErcSettings().IsTestEnabled( ERCE_GLOBLABEL ) && isGlobal )
2525  return true;
2526 
2527  wxString name = EscapeString( text->GetShownText(), CTX_NETNAME );
2528 
2529  if( isGlobal )
2530  {
2531  // This will be set to true if the global is connected to a pin above, but we
2532  // want to reset this to false so that globals get flagged if they only have a
2533  // single instance
2534  has_other_connections = false;
2535 
2536  if( m_net_name_to_subgraphs_map.count( name ) )
2537  {
2538  if( m_net_name_to_subgraphs_map.at( name ).size() > 1 )
2539  has_other_connections = true;
2540  else if( aSubgraph->m_multiple_drivers )
2541  has_other_connections = true;
2542  }
2543  }
2544  else if( text->Type() == SCH_HIER_LABEL_T )
2545  {
2546  // For a hier label, check if the parent pin is connected
2547  if( aSubgraph->m_hier_parent &&
2548  ( aSubgraph->m_hier_parent->m_strong_driver ||
2549  aSubgraph->m_hier_parent->m_drivers.size() > 1))
2550  {
2551  // For now, a simple check: if there is more than one driver, the parent is probably
2552  // connected elsewhere (because at least one driver will be the hier pin itself)
2553  has_other_connections = true;
2554  }
2555  }
2556  else
2557  {
2558  auto pair = std::make_pair( aSubgraph->m_sheet, name );
2559 
2560  if( m_local_label_cache.count( pair ) && m_local_label_cache.at( pair ).size() > 1 )
2561  has_other_connections = true;
2562  }
2563 
2564  if( !has_other_connections )
2565  {
2566  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( isGlobal ? ERCE_GLOBLABEL
2568  ercItem->SetItems( text );
2569 
2570  SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
2571  aSubgraph->m_sheet.LastScreen()->Append( marker );
2572 
2573  return false;
2574  }
2575 
2576  return true;
2577 }
SCH_SHEET_PATH m_sheet
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:115
SCHEMATIC * m_schematic
The schematic this graph represents.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
std::vector< SCH_ITEM * > m_items
bool m_strong_driver
True if the driver is "strong": a label or power object.
std::vector< SCH_ITEM * > m_drivers
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_text.cpp:483
wxPoint GetPosition() const override
Definition: sch_text.h:313
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:131
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
These Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which ar...
Definition: string.cpp:77
CONNECTION_SUBGRAPH * m_hier_parent
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:131
SCH_ITEM * m_no_connect
No-connect item in graph, if any.
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193

References SCH_SCREEN::Append(), ERC_ITEM::Create(), CTX_NETNAME, ERCE_GLOBLABEL, ERCE_LABEL_NOT_CONNECTED, SCHEMATIC::ErcSettings(), EscapeString(), SCH_TEXT::GetPosition(), SCH_TEXT::GetShownText(), ERC_SETTINGS::IsTestEnabled(), SCH_SHEET_PATH::LastScreen(), CONNECTION_SUBGRAPH::m_drivers, CONNECTION_SUBGRAPH::m_hier_parent, CONNECTION_SUBGRAPH::m_items, m_local_label_cache, CONNECTION_SUBGRAPH::m_multiple_drivers, m_net_name_to_subgraphs_map, CONNECTION_SUBGRAPH::m_no_connect, m_schematic, CONNECTION_SUBGRAPH::m_sheet, CONNECTION_SUBGRAPH::m_strong_driver, name, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T, SCH_PIN_T, SCH_SHEET_PIN_T, and EDA_ITEM::Type().

Referenced by RunERC().

◆ ercCheckNoConnects()

bool CONNECTION_GRAPH::ercCheckNoConnects ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for proper presence or absence of no-connect symbols.

A pin with a no-connect symbol should not have any connections A pin without a no-connect symbol should have at least one connection

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2292 of file connection_graph.cpp.

2293 {
2294  wxString msg;
2295  const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2296  SCH_SCREEN* screen = sheet.LastScreen();
2297  bool ok = true;
2298 
2299  if( aSubgraph->m_no_connect != nullptr )
2300  {
2301  bool has_invalid_items = false;
2302  bool has_other_items = false;
2303  SCH_PIN* pin = nullptr;
2304  std::vector<SCH_ITEM*> invalid_items;
2305 
2306  // Any subgraph that contains both a pin and a no-connect should not
2307  // contain any other driving items.
2308 
2309  for( auto item : aSubgraph->m_items )
2310  {
2311  switch( item->Type() )
2312  {
2313  case SCH_PIN_T:
2314  pin = static_cast<SCH_PIN*>( item );
2315  has_other_items = true;
2316  break;
2317 
2318  case SCH_LINE_T:
2319  case SCH_JUNCTION_T:
2320  case SCH_NO_CONNECT_T:
2321  break;
2322 
2323  default:
2324  has_invalid_items = true;
2325  has_other_items = true;
2326  invalid_items.push_back( item );
2327  }
2328  }
2329 
2330  if( pin && has_invalid_items )
2331  {
2332  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
2333  ercItem->SetItems( pin );
2334 
2335  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2336  screen->Append( marker );
2337 
2338  ok = false;
2339  }
2340 
2341  if( !has_other_items )
2342  {
2343  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
2344  ercItem->SetItems( aSubgraph->m_no_connect );
2345 
2346  SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
2347  screen->Append( marker );
2348 
2349  ok = false;
2350  }
2351  }
2352  else
2353  {
2354  bool has_other_connections = false;
2355  std::vector<SCH_PIN*> pins;
2356 
2357  // Any subgraph that lacks a no-connect and contains a pin should also
2358  // contain at least one other potential driver
2359 
2360  for( SCH_ITEM* item : aSubgraph->m_items )
2361  {
2362  switch( item->Type() )
2363  {
2364  case SCH_PIN_T:
2365  {
2366  if( !pins.empty() )
2367  has_other_connections = true;
2368 
2369  pins.emplace_back( static_cast<SCH_PIN*>( item ) );
2370 
2371  break;
2372  }
2373 
2374  default:
2375  if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
2376  has_other_connections = true;
2377 
2378  break;
2379  }
2380  }
2381 
2382  // For many checks, we can just use the first pin
2383  SCH_PIN* pin = pins.empty() ? nullptr : pins[0];
2384 
2385  // Check if invisible power input pins connect to anything else via net name,
2386  // but not for power symbols as the ones in the standard library all have invisible pins
2387  // and we want to throw unconnected errors for those even if they are connected to other
2388  // net items by name, because usually failing to connect them graphically is a mistake
2389  if( pin && !has_other_connections
2391  && !pin->IsVisible()
2392  && !pin->GetLibPin()->GetParent()->IsPower() )
2393  {
2394  wxString name = pin->Connection( sheet )->Name();
2395  wxString local_name = pin->Connection( sheet )->Name( true );
2396 
2397  if( m_global_label_cache.count( name ) ||
2398  ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
2399  {
2400  has_other_connections = true;
2401  }
2402  }
2403 
2404  // Only one pin, and it's not a no-connect pin
2405  if( pin && !has_other_connections && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC )
2406  {
2407  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2408  ercItem->SetItems( pin );
2409 
2410  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2411  screen->Append( marker );
2412 
2413  ok = false;
2414  }
2415 
2416  // If there are multiple pins in this SG, they might be indirectly connected (by netname)
2417  // rather than directly connected (by wires). We want to flag dangling pins even if they
2418  // join nets with another pin, as it's often a mistake
2419  if( pins.size() > 1 )
2420  {
2421  for( SCH_PIN* testPin : pins )
2422  {
2423  // We only apply this test to power symbols, because other symbols have invisible
2424  // pins that are meant to be dangling, but the KiCad standard library power symbols
2425  // have invisible pins that are *not* meant to be dangling.
2426  if( testPin->GetLibPin()->GetParent()->IsPower() &&
2427  testPin->ConnectedItems( sheet ).empty() )
2428  {
2429  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2430  ercItem->SetItems( testPin );
2431 
2432  SCH_MARKER* marker = new SCH_MARKER( ercItem,
2433  testPin->GetTransformedPosition() );
2434  screen->Append( marker );
2435 
2436  ok = false;
2437  }
2438  }
2439  }
2440  }
2441 
2442  return ok;
2443 }
power input (GND, VCC for ICs). Must be connected to a power output.
SCH_SHEET_PATH m_sheet
LIB_PIN * GetLibPin() const
Definition: sch_pin.h:67
bool IsVisible() const
Definition: sch_pin.h:108
LIB_PART * GetParent() const
Definition: lib_item.h:182
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
wxString Name(bool aIgnoreSheet=false) const
std::vector< SCH_ITEM * > m_items
SCH_SHEET_PATH.
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:150
bool IsPower() const
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:131
ELECTRICAL_PINTYPE GetType() const
Definition: sch_pin.cpp:74
SCH_ITEM * m_no_connect
No-connect item in graph, if any.
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
not connected (must be left open)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
wxPoint GetTransformedPosition() const
Returns the pin's position in global coordinates.
Definition: sch_pin.cpp:252

References SCH_SCREEN::Append(), SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_NOCONNECT_CONNECTED, ERCE_NOCONNECT_NOT_CONNECTED, ERCE_PIN_NOT_CONNECTED, CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_PIN::GetLibPin(), LIB_ITEM::GetParent(), EDA_ITEM::GetPosition(), SCH_PIN::GetTransformedPosition(), SCH_PIN::GetType(), LIB_PART::IsPower(), SCH_PIN::IsVisible(), SCH_SHEET_PATH::LastScreen(), m_global_label_cache, CONNECTION_SUBGRAPH::m_items, m_local_label_cache, CONNECTION_SUBGRAPH::m_no_connect, CONNECTION_SUBGRAPH::m_sheet, name, SCH_CONNECTION::Name(), CONNECTION_SUBGRAPH::NONE, PT_NC, PT_POWER_IN, SCH_JUNCTION_T, SCH_LINE_T, SCH_NO_CONNECT_T, SCH_PIN_T, and EDA_ITEM::Type().

Referenced by RunERC().

◆ FindFirstSubgraphByName()

CONNECTION_SUBGRAPH * CONNECTION_GRAPH::FindFirstSubgraphByName ( const wxString &  aNetName)

Retrieves a subgraph for the given net name, if one exists.

Searches every sheet

Parameters
aNetNameis the full net name to search for
Returns
the subgraph matching the query, or nullptr if none is found

Definition at line 2000 of file connection_graph.cpp.

2001 {
2002  if( !m_net_name_to_subgraphs_map.count( aNetName ) )
2003  return nullptr;
2004 
2005  wxASSERT( !m_net_name_to_subgraphs_map.at( aNetName ).empty() );
2006 
2007  return m_net_name_to_subgraphs_map.at( aNetName )[0];
2008 }
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map

References m_net_name_to_subgraphs_map.

◆ FindSubgraphByName()

CONNECTION_SUBGRAPH * CONNECTION_GRAPH::FindSubgraphByName ( const wxString &  aNetName,
const SCH_SHEET_PATH aPath 
)

Returns the subgraph for a given net name on a given sheet.

Parameters
aNetNameis the local net name to look for
aPathis a sheet path to look on
Returns
the subgraph matching the query, or nullptr if none is found

Definition at line 1981 of file connection_graph.cpp.

1983 {
1984  if( !m_net_name_to_subgraphs_map.count( aNetName ) )
1985  return nullptr;
1986 
1987  for( auto sg : m_net_name_to_subgraphs_map.at( aNetName ) )
1988  {
1989  // Cache is supposed to be valid by now
1990  wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
1991 
1992  if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
1993  return sg;
1994  }
1995 
1996  return nullptr;
1997 }
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map

References m_net_name_to_subgraphs_map.

Referenced by NETLIST_EXPORTER::CreatePinList(), and NETLIST_EXPORTER::findAllUnitsOfComponent().

◆ GetBusAlias()

std::shared_ptr< BUS_ALIAS > CONNECTION_GRAPH::GetBusAlias ( const wxString &  aName)

Returns a bus alias pointer for the given name if it exists (from cache)

CONNECTION_GRAPH caches these, they are owned by the SCH_SCREEN that the alias was defined on. The cache is only used to update the graph.

Definition at line 1924 of file connection_graph.cpp.

1925 {
1926  if( m_bus_alias_cache.count( aName ) )
1927  return m_bus_alias_cache.at( aName );
1928 
1929  return nullptr;
1930 }
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache

References m_bus_alias_cache.

Referenced by SCH_CONNECTION::AppendInfoToMsgPanel(), and SCH_CONNECTION::ConfigureFromLabel().

◆ GetBusesNeedingMigration()

std::vector< const CONNECTION_SUBGRAPH * > CONNECTION_GRAPH::GetBusesNeedingMigration ( )

Determines which subgraphs have more than one conflicting bus label.

See also
DIALOG_MIGRATE_BUSES
Returns
a list of subgraphs that need migration

Definition at line 1933 of file connection_graph.cpp.

1934 {
1935  std::vector<const CONNECTION_SUBGRAPH*> ret;
1936 
1937  for( auto&& subgraph : m_subgraphs )
1938  {
1939  // Graph is supposed to be up-to-date before calling this
1940  wxASSERT( !subgraph->m_dirty );
1941 
1942  if( !subgraph->m_driver )
1943  continue;
1944 
1945  auto sheet = subgraph->m_sheet;
1946  auto connection = subgraph->m_driver->Connection( sheet );
1947 
1948  if( !connection->IsBus() )
1949  continue;
1950 
1951  auto labels = subgraph->GetBusLabels();
1952 
1953  if( labels.size() > 1 )
1954  {
1955  bool different = false;
1956  wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
1957 
1958  for( unsigned i = 1; i < labels.size(); ++i )
1959  {
1960  if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
1961  {
1962  different = true;
1963  break;
1964  }
1965  }
1966 
1967  if( !different )
1968  continue;
1969 
1970  wxLogTrace( "CONN", "SG %ld (%s) has multiple bus labels", subgraph->m_code,
1971  connection->Name() );
1972 
1973  ret.push_back( subgraph );
1974  }
1975  }
1976 
1977  return ret;
1978 }
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs

References m_subgraphs.

Referenced by DIALOG_MIGRATE_BUSES::loadGraphData().

◆ getDefaultConnection()

std::shared_ptr< SCH_CONNECTION > CONNECTION_GRAPH::getDefaultConnection ( SCH_ITEM aItem,
CONNECTION_SUBGRAPH aSubgraph 
)
private

Builds a new default connection for the given item based on its properties.

Handles strong drivers (power pins and labels) only

Parameters
aItemis an item that can generate a connection name
aSubgraphis used to determine the sheet to use and retrieve the cached name
Returns
a connection generated from the item, or nullptr if item is not valid

Definition at line 1817 of file connection_graph.cpp.

1819 {
1820  auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
1821 
1822  switch( aItem->Type() )
1823  {
1824  case SCH_PIN_T:
1825  {
1826  auto pin = static_cast<SCH_PIN*>( aItem );
1827 
1828  if( pin->IsPowerConnection() )
1829  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1830 
1831  break;
1832  }
1833 
1834  case SCH_GLOBAL_LABEL_T:
1835  case SCH_HIER_LABEL_T:
1836  case SCH_LABEL_T:
1837  {
1838  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1839  break;
1840  }
1841 
1842  default:
1843  break;
1844  }
1845 
1846  if( c )
1847  {
1848  c->SetGraph( this );
1849  c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
1850  }
1851 
1852  return c;
1853 }
SCH_SHEET_PATH m_sheet
const wxString & GetNameForDriver(SCH_ITEM *aItem)
Returns the candidate net name for a driver.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193

References CONNECTION_SUBGRAPH::GetNameForDriver(), CONNECTION_SUBGRAPH::m_sheet, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T, SCH_PIN_T, and EDA_ITEM::Type().

Referenced by buildConnectionGraph(), and propagateToNeighbors().

◆ GetNetMap()

◆ GetSubgraphForItem()

CONNECTION_SUBGRAPH * CONNECTION_GRAPH::GetSubgraphForItem ( SCH_ITEM aItem)

Definition at line 2011 of file connection_graph.cpp.

2012 {
2013  if( !m_item_to_subgraph_map.count( aItem ) )
2014  return nullptr;
2015 
2016  CONNECTION_SUBGRAPH* ret = m_item_to_subgraph_map.at( aItem );
2017 
2018  while( ret->m_absorbed )
2019  ret = ret->m_absorbed_by;
2020 
2021  return ret;
2022 }
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
A subgraph is a set of items that are electrically connected on a single sheet.
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map

References CONNECTION_SUBGRAPH::m_absorbed, CONNECTION_SUBGRAPH::m_absorbed_by, and m_item_to_subgraph_map.

Referenced by SCH_EDITOR_CONTROL::UpdateNetHighlighting().

◆ matchBusMember()

SCH_CONNECTION * CONNECTION_GRAPH::matchBusMember ( SCH_CONNECTION aBusConnection,
SCH_CONNECTION aSearch 
)
staticprivate

Search for a matching bus member inside a bus connection.

For bus groups, this returns a bus member that matches aSearch by name. For bus vectors, this returns a bus member that matches by vector index.

Parameters
aBusConnectionis the bus connection to search
aSearchis the net connection to search for
Returns
a member of aBusConnection that matches aSearch

Definition at line 1856 of file connection_graph.cpp.

1858 {
1859  wxASSERT( aBusConnection->IsBus() );
1860 
1861  SCH_CONNECTION* match = nullptr;
1862 
1863  if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
1864  {
1865  // Vector bus: compare against index, because we allow the name
1866  // to be different
1867 
1868  for( const auto& bus_member : aBusConnection->Members() )
1869  {
1870  if( bus_member->VectorIndex() == aSearch->VectorIndex() )
1871  {
1872  match = bus_member.get();
1873  break;
1874  }
1875  }
1876  }
1877  else
1878  {
1879  // Group bus
1880  for( const auto& c : aBusConnection->Members() )
1881  {
1882  // Vector inside group: compare names, because for bus groups
1883  // we expect the naming to be consistent across all usages
1884  // TODO(JE) explain this in the docs
1885  if( c->Type() == CONNECTION_TYPE::BUS )
1886  {
1887  for( const auto& bus_member : c->Members() )
1888  {
1889  if( bus_member->LocalName() == aSearch->LocalName() )
1890  {
1891  match = bus_member.get();
1892  break;
1893  }
1894  }
1895  }
1896  else if( c->LocalName() == aSearch->LocalName() )
1897  {
1898  match = c.get();
1899  break;
1900  }
1901  }
1902  }
1903 
1904  return match;
1905 }
wxString LocalName() const
long VectorIndex() const
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
bool IsBus() const
CONNECTION_TYPE Type() const
This item represents a bus vector.

References BUS, SCH_CONNECTION::IsBus(), SCH_CONNECTION::LocalName(), SCH_CONNECTION::Members(), SCH_CONNECTION::Type(), and SCH_CONNECTION::VectorIndex().

Referenced by buildConnectionGraph(), and propagateToNeighbors().

◆ propagateToNeighbors()

void CONNECTION_GRAPH::propagateToNeighbors ( CONNECTION_SUBGRAPH aSubgraph)
private

Updates all neighbors of a subgraph with this one's connectivity info.

If this subgraph contains hierarchical links, this method will descent the hierarchy and propagate the connectivity across all linked sheets.

Definition at line 1545 of file connection_graph.cpp.

1546 {
1547  SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
1548  std::vector<CONNECTION_SUBGRAPH*> search_list;
1549  std::unordered_set<CONNECTION_SUBGRAPH*> visited;
1550  std::vector<SCH_CONNECTION*> stale_bus_members;
1551 
1552  auto visit = [&] ( CONNECTION_SUBGRAPH* aParent ) {
1553  for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
1554  {
1555  SCH_SHEET_PATH path = aParent->m_sheet;
1556  path.push_back( pin->GetParent() );
1557 
1558  if( !m_sheet_to_subgraphs_map.count( path ) )
1559  continue;
1560 
1561  for( CONNECTION_SUBGRAPH* candidate : m_sheet_to_subgraphs_map.at( path ) )
1562  {
1563  if( !candidate->m_strong_driver ||
1564  candidate->m_hier_ports.empty() ||
1565  visited.count( candidate ) )
1566  continue;
1567 
1568  for( SCH_HIERLABEL* label : candidate->m_hier_ports )
1569  {
1570  if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
1571  {
1572  wxLogTrace( "CONN", "%lu: found child %lu (%s)", aParent->m_code,
1573  candidate->m_code, candidate->m_driver_connection->Name() );
1574 
1575  candidate->m_hier_parent = aParent;
1576 
1577  search_list.push_back( candidate );
1578  break;
1579  }
1580  }
1581  }
1582  }
1583 
1584  for( SCH_HIERLABEL* label : aParent->m_hier_ports )
1585  {
1586  SCH_SHEET_PATH path = aParent->m_sheet;
1587  path.pop_back();
1588 
1589  if( !m_sheet_to_subgraphs_map.count( path ) )
1590  continue;
1591 
1592  for( CONNECTION_SUBGRAPH* candidate : m_sheet_to_subgraphs_map.at( path ) )
1593  {
1594  if( candidate->m_hier_pins.empty() ||
1595  visited.count( candidate ) ||
1596  ( candidate->m_driver_connection->Type() !=
1597  aParent->m_driver_connection->Type() ) )
1598  continue;
1599 
1600  for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
1601  {
1602  SCH_SHEET_PATH pin_path = path;
1603  pin_path.push_back( pin->GetParent() );
1604 
1605  if( pin_path != aParent->m_sheet )
1606  continue;
1607 
1608  if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
1609  {
1610  wxLogTrace( "CONN", "%lu: found additional parent %lu (%s)",
1611  aParent->m_code, candidate->m_code,
1612  candidate->m_driver_connection->Name() );
1613 
1614  search_list.push_back( candidate );
1615  break;
1616  }
1617  }
1618  }
1619  }
1620  };
1621 
1622  auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
1623  for( const auto& kv : aParentGraph->m_bus_neighbors )
1624  {
1625  for( CONNECTION_SUBGRAPH* neighbor : kv.second )
1626  {
1627  // May have been absorbed but won't have been deleted
1628  while( neighbor->m_absorbed )
1629  neighbor = neighbor->m_absorbed_by;
1630 
1631  SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
1632 
1633  // Now member may be out of date, since we just cloned the
1634  // connection from higher up in the hierarchy. We need to
1635  // figure out what the actual new connection is.
1636  SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
1637 
1638  if( !member )
1639  {
1640  // Try harder: we might match on a secondary driver
1641  for( CONNECTION_SUBGRAPH* sg : kv.second )
1642  {
1643  if( sg->m_multiple_drivers )
1644  {
1645  SCH_SHEET_PATH sheet = sg->m_sheet;
1646 
1647  for( SCH_ITEM* driver : sg->m_drivers )
1648  {
1649  auto c = getDefaultConnection( driver, sg );
1650  member = matchBusMember( parent, c.get() );
1651 
1652  if( member )
1653  break;
1654  }
1655  }
1656 
1657  if( member )
1658  break;
1659  }
1660  }
1661 
1662  // This is bad, probably an ERC error
1663  if( !member )
1664  {
1665  wxLogTrace( "CONN", "Could not match bus member %s in %s",
1666  kv.first->Name(), parent->Name() );
1667  continue;
1668  }
1669 
1670  auto neighbor_conn = neighbor->m_driver_connection;
1671  auto neighbor_name = neighbor_conn->Name();
1672 
1673  // Matching name: no update needed
1674  if( neighbor_name == member->Name() )
1675  continue;
1676 
1677  // Safety check against infinite recursion
1678  wxASSERT( neighbor_conn->IsNet() );
1679 
1680  wxLogTrace( "CONN", "%lu (%s) connected to bus member %s (local %s)",
1681  neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
1682 
1683  // Take whichever name is higher priority
1686  {
1687  member->Clone( *neighbor_conn );
1688  stale_bus_members.push_back( member );
1689  }
1690  else
1691  {
1692  neighbor_conn->Clone( *member );
1693  neighbor->UpdateItemConnections();
1694 
1695  recacheSubgraphName( neighbor, neighbor_name );
1696 
1697  // Recurse onto this neighbor in case it needs to re-propagate
1698  neighbor->m_dirty = true;
1699  propagateToNeighbors( neighbor );
1700  }
1701  }
1702  }
1703  };
1704 
1705  // If we are a bus, we must propagate to local neighbors and then the hierarchy
1706  if( conn->IsBus() )
1707  propagate_bus_neighbors( aSubgraph );
1708 
1709  // If we don't have any hier pins (i.e. no children), nothing to do
1710  if( aSubgraph->m_hier_pins.empty() )
1711  {
1712  // If we also don't have any parents, we'll never be visited again
1713  if( aSubgraph->m_hier_ports.empty() )
1714  aSubgraph->m_dirty = false;
1715 
1716  return;
1717  }
1718 
1719  // If we do have hier ports, skip this subgraph as it will be visited by a parent
1720  // TODO(JE) this will leave the subgraph dirty if there is no matching parent subgraph,
1721  // which should be flagged as an ERC error
1722  if( !aSubgraph->m_hier_ports.empty() )
1723  return;
1724 
1725  visited.insert( aSubgraph );
1726 
1727  wxLogTrace( "CONN", "Propagating %lu (%s) to subsheets",
1728  aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
1729 
1730  visit( aSubgraph );
1731 
1732  for( unsigned i = 0; i < search_list.size(); i++ )
1733  {
1734  auto child = search_list[i];
1735 
1736  visited.insert( child );
1737 
1738  visit( child );
1739 
1740  child->m_dirty = false;
1741  }
1742 
1743  // Now, find the best driver for this chain of subgraphs
1744  CONNECTION_SUBGRAPH* driver = aSubgraph;
1747 
1748  // Check if a subsheet has a higher-priority connection to the same net
1750  {
1751  for( CONNECTION_SUBGRAPH* subgraph : visited )
1752  {
1754  CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
1755 
1756  // Upgrade driver to be this subgraph if this subgraph has a power pin or global
1757  // Also upgrade if we found something with a shorter sheet path (higher in hierarchy)
1758  // but with an equivalent priority
1759 
1760  if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
1761  ( priority >= highest && subgraph->m_sheet.size() < aSubgraph->m_sheet.size() ) )
1762  driver = subgraph;
1763  }
1764  }
1765 
1766  if( driver != aSubgraph )
1767  {
1768  wxLogTrace( "CONN", "%lu (%s) overridden by new driver %lu (%s)",
1769  aSubgraph->m_code, aSubgraph->m_driver_connection->Name(),
1770  driver->m_code, driver->m_driver_connection->Name() );
1771  }
1772 
1773  conn = driver->m_driver_connection;
1774 
1775  for( CONNECTION_SUBGRAPH* subgraph : visited )
1776  {
1777  wxString old_name = subgraph->m_driver_connection->Name();
1778 
1779  subgraph->m_driver_connection->Clone( *conn );
1780  subgraph->UpdateItemConnections();
1781 
1782  if( old_name != conn->Name() )
1783  recacheSubgraphName( subgraph, old_name );
1784 
1785  if( conn->IsBus() )
1786  propagate_bus_neighbors( subgraph );
1787  }
1788 
1789  // Somewhere along the way, a bus member may have been upgraded to a global or power label.
1790  // Because this can happen anywhere, we need a second pass to update all instances of that bus
1791  // member to have the correct connection info
1792  if( conn->IsBus() && !stale_bus_members.empty() )
1793  {
1794  for( auto stale_member : stale_bus_members )
1795  {
1796  for( CONNECTION_SUBGRAPH* subgraph : visited )
1797  {
1798  SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
1799  stale_member );
1800  wxASSERT( member );
1801 
1802  wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", subgraph->m_code,
1803  subgraph->m_driver_connection->Name(), member->LocalName(),
1804  stale_member->Name() );
1805 
1806  member->Clone( *stale_member );
1807 
1808  propagate_bus_neighbors( subgraph );
1809  }
1810  }
1811  }
1812 
1813  aSubgraph->m_dirty = false;
1814 }
SCH_SHEET_PATH m_sheet
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph)
Updates all neighbors of a subgraph with this one's connectivity info.
SCH_SHEET * GetParent() const
Get the parent sheet object of this sheet pin.
Definition: sch_sheet.h:168
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
std::vector< SCH_SHEET_PIN * > m_hier_pins
wxString LocalName() const
#define kv
A subgraph is a set of items that are electrically connected on a single sheet.
void pop_back()
Forwarded method from std::vector.
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
void recacheSubgraphName(CONNECTION_SUBGRAPH *aSubgraph, const wxString &aOldName)
wxString Name(bool aIgnoreSheet=false) const
size_t size() const
Forwarded method from std::vector.
std::vector< SCH_ITEM * > m_drivers
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
SCH_SHEET_PATH.
void Clone(SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
std::vector< SCH_HIERLABEL * > m_hier_ports
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
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.
bool IsBus() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
static SCH_CONNECTION * matchBusMember(SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
Search for a matching bus member inside a bus connection.
void UpdateItemConnections()
Updates all items to match the driver connection.

References SCH_CONNECTION::Clone(), getDefaultConnection(), CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_SHEET_PIN::GetParent(), SCH_CONNECTION::IsBus(), kv, SCH_CONNECTION::LocalName(), CONNECTION_SUBGRAPH::m_absorbed, CONNECTION_SUBGRAPH::m_absorbed_by, CONNECTION_SUBGRAPH::m_code, CONNECTION_SUBGRAPH::m_dirty, CONNECTION_SUBGRAPH::m_driver, CONNECTION_SUBGRAPH::m_driver_connection, CONNECTION_SUBGRAPH::m_drivers, CONNECTION_SUBGRAPH::m_hier_pins, CONNECTION_SUBGRAPH::m_hier_ports, CONNECTION_SUBGRAPH::m_multiple_drivers, CONNECTION_SUBGRAPH::m_sheet, m_sheet_to_subgraphs_map, matchBusMember(), SCH_CONNECTION::Name(), SCH_SHEET_PATH::pop_back(), CONNECTION_SUBGRAPH::POWER_PIN, SCH_SHEET_PATH::push_back(), recacheSubgraphName(), SCH_SHEET_PATH::size(), and CONNECTION_SUBGRAPH::UpdateItemConnections().

Referenced by buildConnectionGraph().

◆ recacheSubgraphName()

void CONNECTION_GRAPH::recacheSubgraphName ( CONNECTION_SUBGRAPH aSubgraph,
const wxString &  aOldName 
)
private

Definition at line 1908 of file connection_graph.cpp.

1910 {
1911  if( m_net_name_to_subgraphs_map.count( aOldName ) )
1912  {
1913  auto& vec = m_net_name_to_subgraphs_map.at( aOldName );
1914  vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
1915  }
1916 
1917  wxLogTrace( "CONN", "recacheSubgraphName: %s => %s", aOldName,
1918  aSubgraph->m_driver_connection->Name() );
1919 
1920  m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
1921 }
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
wxString Name(bool aIgnoreSheet=false) const

References CONNECTION_SUBGRAPH::m_driver_connection, m_net_name_to_subgraphs_map, and SCH_CONNECTION::Name().

Referenced by propagateToNeighbors().

◆ Recalculate()

void CONNECTION_GRAPH::Recalculate ( const SCH_SHEET_LIST aSheetList,
bool  aUnconditional = false 
)

Updates the connection graph for the given list of sheets.

Parameters
aSheetListis the list of possibly modified sheets
aUnconditionalis true if an unconditional full recalculation should be done

Definition at line 387 of file connection_graph.cpp.

388 {
389  PROF_COUNTER recalc_time( "CONNECTION_GRAPH::Recalculate" );
390 
391  if( aUnconditional )
392  Reset();
393 
394  PROF_COUNTER update_items( "updateItemConnectivity" );
395 
396  m_sheetList = aSheetList;
397 
398  for( const SCH_SHEET_PATH& sheet : aSheetList )
399  {
400  std::vector<SCH_ITEM*> items;
401 
402  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
403  {
404  if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
405  items.push_back( item );
406  }
407 
408  m_items.reserve( m_items.size() + items.size() );
409 
410  updateItemConnectivity( sheet, items );
411 
412  // UpdateDanglingState() also adds connected items for SCH_TEXT
413  sheet.LastScreen()->TestDanglingEnds( &sheet );
414  }
415 
416  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
417  update_items.Show();
418 
419  PROF_COUNTER build_graph( "buildConnectionGraph" );
420 
422 
423  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
424  build_graph.Show();
425 
426  recalc_time.Stop();
427 
428  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
429  recalc_time.Show();
430 
431 #ifndef DEBUG
432  // Pressure relief valve for release builds
433  const double max_recalc_time_msecs = 250.;
434 
435  if( m_allowRealTime && ADVANCED_CFG::GetCfg().m_realTimeConnectivity &&
436  recalc_time.msecs() > max_recalc_time_msecs )
437  {
438  m_allowRealTime = false;
439  }
440 #endif
441 }
void buildConnectionGraph()
Generates the connection graph (after all item connectivity has been updated)
std::vector< SCH_ITEM * > m_items
The class PROF_COUNTER is a small class to help profiling.
Definition: profile.h:44
void updateItemConnectivity(const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
Updates the graphical connectivity between items (i.e.
SCH_SHEET_PATH.
static const wxChar ConnProfileMask[]
static bool m_allowRealTime
SCH_SHEET_LIST m_sheetList
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194

References buildConnectionGraph(), ConnProfileMask, ADVANCED_CFG::GetCfg(), m_allowRealTime, m_items, m_sheetList, PROF_COUNTER::msecs(), Reset(), PROF_COUNTER::Show(), PROF_COUNTER::Stop(), and updateItemConnectivity().

Referenced by TEST_NETLISTS_FIXTURE::loadSchematic(), SCH_EDIT_FRAME::RecalculateConnections(), and SCH_SCREENS::UpdateSymbolLinks().

◆ Reset()

void CONNECTION_GRAPH::Reset ( )

Definition at line 363 of file connection_graph.cpp.

364 {
365  for( auto& subgraph : m_subgraphs )
366  delete subgraph;
367 
368  m_items.clear();
369  m_subgraphs.clear();
370  m_driver_subgraphs.clear();
371  m_sheet_to_subgraphs_map.clear();
372  m_invisible_power_pins.clear();
373  m_bus_alias_cache.clear();
374  m_net_name_to_code_map.clear();
375  m_bus_name_to_code_map.clear();
378  m_item_to_subgraph_map.clear();
379  m_local_label_cache.clear();
380  m_global_label_cache.clear();
381  m_last_net_code = 1;
382  m_last_bus_code = 1;
384 }
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
NET_MAP m_net_code_to_subgraphs_map
std::vector< SCH_ITEM * > m_items
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
std::map< wxString, int > m_bus_name_to_code_map
std::map< wxString, int > m_net_name_to_code_map
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map

References m_bus_alias_cache, m_bus_name_to_code_map, m_driver_subgraphs, m_global_label_cache, m_invisible_power_pins, m_item_to_subgraph_map, m_items, m_last_bus_code, m_last_net_code, m_last_subgraph_code, m_local_label_cache, m_net_code_to_subgraphs_map, m_net_name_to_code_map, m_net_name_to_subgraphs_map, m_sheet_to_subgraphs_map, and m_subgraphs.

Referenced by SCH_EDIT_FRAME::OpenProjectFiles(), Recalculate(), SCHEMATIC::Reset(), SCHEMATIC::SetRoot(), and ~CONNECTION_GRAPH().

◆ RunERC()

int CONNECTION_GRAPH::RunERC ( )

Runs electrical rule checks on the connectivity graph.

Precondition: graph is up-to-date

Returns
the number of errors found

NOTE:

We could check that labels attached to bus subgraphs follow the proper format (i.e. actually define a bus).

This check doesn't need to be here right now because labels won't actually be connected to bus wires if they aren't in the right format due to their TestDanglingEnds() implementation.

Definition at line 2025 of file connection_graph.cpp.

2026 {
2027  int error_count = 0;
2028 
2029  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2030 
2031  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2032 
2033  for( auto&& subgraph : m_subgraphs )
2034  {
2035  // Graph is supposed to be up-to-date before calling RunERC()
2036  wxASSERT( !subgraph->m_dirty );
2037 
2049  if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) && !subgraph->ResolveDrivers() )
2050  error_count++;
2051 
2052  if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT )
2053  && !ercCheckBusToNetConflicts( subgraph ) )
2054  error_count++;
2055 
2056  if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT )
2057  && !ercCheckBusToBusEntryConflicts( subgraph ) )
2058  error_count++;
2059 
2060  if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT )
2061  && !ercCheckBusToBusConflicts( subgraph ) )
2062  error_count++;
2063 
2064  if( settings.IsTestEnabled( ERCE_WIRE_DANGLING )
2065  && !ercCheckFloatingWires( subgraph ) )
2066  error_count++;
2067 
2068  // The following checks are always performed since they don't currently
2069  // have an option exposed to the user
2070 
2071  if( !ercCheckNoConnects( subgraph ) )
2072  error_count++;
2073 
2074  if( ( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
2075  || settings.IsTestEnabled( ERCE_GLOBLABEL ) ) && !ercCheckLabels( subgraph ) )
2076  error_count++;
2077  }
2078 
2079  // Hierarchical sheet checking is done at the schematic level
2080  if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
2081  error_count += ercCheckHierSheets();
2082 
2083  return error_count;
2084 }
bool ercCheckBusToBusEntryConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting bus entry to bus connections.
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:115
bool ercCheckFloatingWires(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for floating wires.
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
SCHEMATIC * m_schematic
The schematic this graph represents.
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.
bool ercCheckNoConnects(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for proper presence or absence of no-connect symbols.
Container for ERC settings.
Definition: erc_settings.h:88
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:131
bool ercCheckLabels(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for proper connection of labels.
bool ercCheckBusToBusConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting connections between two bus items.

References ercCheckBusToBusConflicts(), ercCheckBusToBusEntryConflicts(), ercCheckBusToNetConflicts(), ercCheckFloatingWires(), ercCheckHierSheets(), ercCheckLabels(), ercCheckNoConnects(), ERCE_BUS_ENTRY_CONFLICT, ERCE_BUS_TO_BUS_CONFLICT, ERCE_BUS_TO_NET_CONFLICT, ERCE_DRIVER_CONFLICT, ERCE_GLOBLABEL, ERCE_HIERACHICAL_LABEL, ERCE_LABEL_NOT_CONNECTED, ERCE_WIRE_DANGLING, SCHEMATIC::ErcSettings(), ERC_SETTINGS::IsTestEnabled(), m_schematic, and m_subgraphs.

Referenced by DIALOG_ERC::testErc().

◆ SetSchematic()

void CONNECTION_GRAPH::SetSchematic ( SCHEMATIC aSchematic)
inline

Definition at line 240 of file connection_graph.h.

241  {
242  m_schematic = aSchematic;
243  }
SCHEMATIC * m_schematic
The schematic this graph represents.

References m_schematic.

◆ updateItemConnectivity()

void CONNECTION_GRAPH::updateItemConnectivity ( const SCH_SHEET_PATH aSheet,
const std::vector< SCH_ITEM * > &  aItemList 
)
private

Updates the graphical connectivity between items (i.e.

where they touch) The items passed in must be on the same sheet.

In the first phase, all items in aItemList have their connections initialized for the given sheet (since they may have connections on more than one sheet, and each needs to be calculated individually). The graphical connection points for the item are added to a map that stores (x, y) -> [list of items].

Any item that is stored in the list of items that have a connection point at a given (x, y) location will eventually be electrically connected. This means that we can't store SCH_COMPONENTs in this map – we must store a structure that links a specific pin on a component back to that component: a SCH_PIN_CONNECTION. This wrapper class is a convenience for linking a pin and component to a specific (x, y) point.

In the second phase, we iterate over each value in the map, which is a vector of items that have overlapping connection points. After some checks to ensure that the items should actually connect, the items are linked together using ConnectedItems().

As a side effect, items are loaded into m_items for BuildConnectionGraph()

Parameters
aSheetis the path to the sheet of all items in the list
aItemListis a list of items to consider

Definition at line 444 of file connection_graph.cpp.

446 {
447  std::map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
448 
449  for( SCH_ITEM* item : aItemList )
450  {
451  std::vector< wxPoint > points = item->GetConnectionPoints();
452  item->ConnectedItems( aSheet ).clear();
453 
454  if( item->Type() == SCH_SHEET_T )
455  {
456  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
457  {
458  if( !pin->Connection( aSheet ) )
459  pin->InitializeConnection( aSheet, this );
460 
461  pin->ConnectedItems( aSheet ).clear();
462  pin->Connection( aSheet )->Reset();
463 
464  connection_map[ pin->GetTextPos() ].push_back( pin );
465  m_items.emplace_back( pin );
466  }
467  }
468  else if( item->Type() == SCH_COMPONENT_T )
469  {
470  SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
471 
472  // TODO(JE) right now this relies on GetPins() returning good SCH_PIN pointers
473  // that contain good LIB_PIN pointers. Since these get invalidated whenever the
474  // library component is refreshed, the current solution as of ed025972 is to just
475  // rebuild the SCH_PIN list when the component is refreshed, and then re-run the
476  // connectivity calculations. This is slow and should be improved before release.
477  // See https://gitlab.com/kicad/code/kicad/issues/3784
478 
479  for( SCH_PIN* pin : component->GetPins( &aSheet ) )
480  {
481  pin->InitializeConnection( aSheet, this );
482 
483  wxPoint pos = pin->GetPosition();
484 
485  // because calling the first time is not thread-safe
486  pin->GetDefaultNetName( aSheet );
487  pin->ConnectedItems( aSheet ).clear();
488 
489  // Invisible power pins need to be post-processed later
490 
491  if( pin->IsPowerConnection() && !pin->IsVisible() )
492  m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
493 
494  connection_map[ pos ].push_back( pin );
495  m_items.emplace_back( pin );
496  }
497  }
498  else
499  {
500  m_items.emplace_back( item );
501  auto conn = item->InitializeConnection( aSheet, this );
502 
503  // Set bus/net property here so that the propagation code uses it
504  switch( item->Type() )
505  {
506  case SCH_LINE_T:
507  conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
509  break;
510 
511  case SCH_BUS_BUS_ENTRY_T:
512  conn->SetType( CONNECTION_TYPE::BUS );
513  // clean previous (old) links:
514  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
515  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
516  break;
517 
518  case SCH_PIN_T:
519  conn->SetType( CONNECTION_TYPE::NET );
520  break;
521 
523  conn->SetType( CONNECTION_TYPE::NET );
524  // clean previous (old) link:
525  static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
526  break;
527 
528  default:
529  break;
530  }
531 
532  for( const wxPoint& point : points )
533  connection_map[ point ].push_back( item );
534  }
535 
536  item->SetConnectivityDirty( false );
537  }
538 
539  for( const auto& it : connection_map )
540  {
541  auto connection_vec = it.second;
542 
543  for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
544  {
545  SCH_ITEM* connected_item = *primary_it;
546 
547  // Bus entries are special: they can have connection points in the
548  // middle of a wire segment, because the junction algo doesn't split
549  // the segment in two where you place a bus entry. This means that
550  // bus entries that don't land on the end of a line segment need to
551  // have "virtual" connection points to the segments they graphically
552  // touch.
553  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
554  {
555  // If this location only has the connection point of the bus
556  // entry itself, this means that either the bus entry is not
557  // connected to anything graphically, or that it is connected to
558  // a segment at some point other than at one of the endpoints.
559  if( connection_vec.size() == 1 )
560  {
561  SCH_SCREEN* screen = aSheet.LastScreen();
562  SCH_LINE* bus = screen->GetBus( it.first );
563 
564  if( bus )
565  {
566  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
567  bus_entry->m_connected_bus_item = bus;
568  }
569  }
570  }
571 
572  // Bus-to-bus entries are treated just like bus wires
573  else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
574  {
575  if( connection_vec.size() < 2 )
576  {
577  SCH_SCREEN* screen = aSheet.LastScreen();
578  SCH_LINE* bus = screen->GetBus( it.first );
579 
580  if( bus )
581  {
582  auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
583 
584  if( it.first == bus_entry->GetPosition() )
585  bus_entry->m_connected_bus_items[0] = bus;
586  else
587  bus_entry->m_connected_bus_items[1] = bus;
588 
589  bus_entry->ConnectedItems( aSheet ).insert( bus );
590  bus->ConnectedItems( aSheet ).insert( bus_entry );
591  }
592  }
593  }
594 
595  // Change junctions to be on bus junction layer if they are touching a bus
596  else if( connected_item->Type() == SCH_JUNCTION_T )
597  {
598  SCH_SCREEN* screen = aSheet.LastScreen();
599  SCH_LINE* bus = screen->GetBus( it.first );
600 
601  connected_item->SetLayer( bus ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
602  }
603 
604  for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
605  {
606  auto test_item = *test_it;
607 
608  if( connected_item != test_item &&
609  connected_item->ConnectionPropagatesTo( test_item ) &&
610  test_item->ConnectionPropagatesTo( connected_item ) )
611  {
612  connected_item->ConnectedItems( aSheet ).insert( test_item );
613  test_item->ConnectedItems( aSheet ).insert( connected_item );
614  }
615 
616  // Set up the link between the bus entry net and the bus
617  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
618  {
619  if( test_item->Connection( aSheet )->IsBus() )
620  {
621  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
622  bus_entry->m_connected_bus_item = test_item;
623  }
624  }
625  }
626 
627  // If we got this far and did not find a connected bus item for a bus entry,
628  // we should do a manual scan in case there is a bus item on this connection
629  // point but we didn't pick it up earlier because there is *also* a net item here.
630  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
631  {
632  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
633 
634  if( !bus_entry->m_connected_bus_item )
635  {
636  auto screen = aSheet.LastScreen();
637  auto bus = screen->GetBus( it.first );
638 
639  if( bus )
640  bus_entry->m_connected_bus_item = bus;
641  }
642  }
643  }
644  }
645 }
virtual bool ConnectionPropagatesTo(const EDA_ITEM *aItem) const
Returns true if this item should propagate connection info to aItem.
Definition: sch_item.h:431
std::vector< SCH_ITEM * > m_items
void SetLayer(SCH_LAYER_ID aLayer)
Set the layer this item is on.
Definition: sch_item.h:286
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
This item represents a net.
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieves a list of the SCH_PINs for the given sheet path.
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
SCH_SCREEN * LastScreen()
Function LastScreen.
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
SCH_ITEM_SET & ConnectedItems(const SCH_SHEET_PATH &aPath)
Retrieves the set of items connected to this item on the given sheet.
Definition: sch_item.cpp:178
Schematic symbol object.
Definition: sch_component.h:80
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
This item represents a bus vector.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
SCH_LINE * GetBus(const wxPoint &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T)
Definition: sch_screen.h:439

References BUS, SCH_ITEM::ConnectedItems(), SCH_ITEM::ConnectionPropagatesTo(), SCH_SCREEN::GetBus(), SCH_COMPONENT::GetPins(), SCH_SHEET_PATH::LastScreen(), LAYER_BUS, LAYER_BUS_JUNCTION, LAYER_JUNCTION, m_invisible_power_pins, m_items, NET, SCH_BUS_BUS_ENTRY_T, SCH_BUS_WIRE_ENTRY_T, SCH_COMPONENT_T, SCH_JUNCTION_T, SCH_LINE_T, SCH_PIN_T, SCH_SHEET_T, SCH_ITEM::SetLayer(), and EDA_ITEM::Type().

Referenced by Recalculate().

Member Data Documentation

◆ m_allowRealTime

◆ m_bus_alias_cache

std::unordered_map< wxString, std::shared_ptr<BUS_ALIAS> > CONNECTION_GRAPH::m_bus_alias_cache
private

Definition at line 322 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetBusAlias(), and Reset().

◆ m_bus_name_to_code_map

std::map<wxString, int> CONNECTION_GRAPH::m_bus_name_to_code_map
private

Definition at line 326 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_driver_subgraphs

std::vector<CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::m_driver_subgraphs
private

Definition at line 314 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_global_label_cache

std::map<wxString, std::vector<const CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_global_label_cache
private

Definition at line 328 of file connection_graph.h.

Referenced by buildConnectionGraph(), ercCheckNoConnects(), and Reset().

◆ m_invisible_power_pins

std::vector<std::pair<SCH_SHEET_PATH, SCH_PIN*> > CONNECTION_GRAPH::m_invisible_power_pins
private

Definition at line 320 of file connection_graph.h.

Referenced by buildConnectionGraph(), Reset(), and updateItemConnectivity().

◆ m_item_to_subgraph_map

std::map<SCH_ITEM*, CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::m_item_to_subgraph_map
private

Definition at line 336 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetSubgraphForItem(), and Reset().

◆ m_items

std::vector<SCH_ITEM*> CONNECTION_GRAPH::m_items
private

◆ m_last_bus_code

int CONNECTION_GRAPH::m_last_bus_code
private

Definition at line 342 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_last_net_code

int CONNECTION_GRAPH::m_last_net_code
private

Definition at line 340 of file connection_graph.h.

Referenced by assignNewNetCode(), and Reset().

◆ m_last_subgraph_code

int CONNECTION_GRAPH::m_last_subgraph_code
private

Definition at line 344 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_local_label_cache

std::map< std::pair<SCH_SHEET_PATH, wxString>, std::vector<const CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_local_label_cache
private

◆ m_net_code_to_subgraphs_map

NET_MAP CONNECTION_GRAPH::m_net_code_to_subgraphs_map
private

Definition at line 338 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetNetMap(), and Reset().

◆ m_net_name_to_code_map

std::map<wxString, int> CONNECTION_GRAPH::m_net_name_to_code_map
private

Definition at line 324 of file connection_graph.h.

Referenced by assignNewNetCode(), and Reset().

◆ m_net_name_to_subgraphs_map

std::unordered_map<wxString, std::vector<CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_net_name_to_subgraphs_map
private

◆ m_schematic

SCHEMATIC* CONNECTION_GRAPH::m_schematic
private

The schematic this graph represents.

Definition at line 346 of file connection_graph.h.

Referenced by buildConnectionGraph(), ercCheckLabels(), RunERC(), and SetSchematic().

◆ m_sheet_to_subgraphs_map

std::unordered_map<SCH_SHEET_PATH, std::vector<CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_sheet_to_subgraphs_map
private

Definition at line 318 of file connection_graph.h.

Referenced by buildConnectionGraph(), propagateToNeighbors(), and Reset().

◆ m_sheetList

SCH_SHEET_LIST CONNECTION_GRAPH::m_sheetList
private

Definition at line 305 of file connection_graph.h.

Referenced by ercCheckHierSheets(), and Recalculate().

◆ m_subgraphs

std::vector<CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::m_subgraphs
private

Definition at line 311 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetBusesNeedingMigration(), Reset(), and RunERC().


The documentation for this class was generated from the following files: