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 230 of file connection_graph.h.

Constructor & Destructor Documentation

◆ CONNECTION_GRAPH()

CONNECTION_GRAPH::CONNECTION_GRAPH ( SCHEMATIC aSchematic = nullptr)
inline

Definition at line 233 of file connection_graph.h.

233  :
234  m_last_net_code( 1 ),
235  m_last_bus_code( 1 ),
237  m_schematic( aSchematic )
238  {}
SCHEMATIC * m_schematic
The schematic this graph represents.

◆ ~CONNECTION_GRAPH()

CONNECTION_GRAPH::~CONNECTION_GRAPH ( )
inline

Definition at line 240 of file connection_graph.h.

241  {
242  Reset();
243  }

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 1575 of file connection_graph.cpp.

1576 {
1577  auto connections_to_check( aConnection->Members() );
1578 
1579  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1580  {
1581  auto member = connections_to_check[i];
1582 
1583  if( member->IsBus() )
1584  {
1585  connections_to_check.insert( connections_to_check.end(),
1586  member->Members().begin(),
1587  member->Members().end() );
1588  continue;
1589  }
1590 
1591  assignNewNetCode( *member );
1592  }
1593 }
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 1553 of file connection_graph.cpp.

1554 {
1555  int code;
1556 
1557  auto it = m_net_name_to_code_map.find( aConnection.Name() );
1558 
1559  if( it == m_net_name_to_code_map.end() )
1560  {
1561  code = m_last_net_code++;
1562  m_net_name_to_code_map[ aConnection.Name() ] = code;
1563  }
1564  else
1565  {
1566  code = it->second;
1567  }
1568 
1569  aConnection.SetNetCode( code );
1570 
1571  return code;
1572 }
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 700 of file connection_graph.cpp.

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

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 2222 of file connection_graph.cpp.

2223 {
2224  wxString msg;
2225  auto sheet = aSubgraph->m_sheet;
2226  auto screen = sheet.LastScreen();
2227 
2228  SCH_ITEM* label = nullptr;
2229  SCH_ITEM* port = nullptr;
2230 
2231  for( auto item : aSubgraph->m_items )
2232  {
2233  switch( item->Type() )
2234  {
2235  case SCH_TEXT_T:
2236  case SCH_GLOBAL_LABEL_T:
2237  {
2238  if( !label && item->Connection( &sheet )->IsBus() )
2239  label = item;
2240  break;
2241  }
2242 
2243  case SCH_SHEET_PIN_T:
2244  case SCH_HIER_LABEL_T:
2245  {
2246  if( !port && item->Connection( &sheet )->IsBus() )
2247  port = item;
2248  break;
2249  }
2250 
2251  default:
2252  break;
2253  }
2254  }
2255 
2256  if( label && port )
2257  {
2258  bool match = false;
2259 
2260  for( const auto& member : label->Connection( &sheet )->Members() )
2261  {
2262  for( const auto& test : port->Connection( &sheet )->Members() )
2263  {
2264  if( test != member && member->Name() == test->Name() )
2265  {
2266  match = true;
2267  break;
2268  }
2269  }
2270 
2271  if( match )
2272  break;
2273  }
2274 
2275  if( !match )
2276  {
2277  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
2278  ercItem->SetItems( label, port );
2279 
2280  SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
2281  screen->Append( marker );
2282 
2283  return false;
2284  }
2285  }
2286 
2287  return true;
2288 }
SCH_SHEET_PATH m_sheet
A connection between bus objects doesn't share at least one net.
Definition: erc_settings.h:58
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:160
virtual wxPoint GetPosition() const
Definition: eda_item.h:325
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:131
std::vector< SCH_ITEM * > m_items
SCH_SCREEN * 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 2291 of file connection_graph.cpp.

2292 {
2293  bool conflict = false;
2294  auto sheet = aSubgraph->m_sheet;
2295  auto screen = sheet.LastScreen();
2296 
2297  SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2298  SCH_ITEM* bus_wire = nullptr;
2299  wxString bus_name;
2300 
2301  for( auto item : aSubgraph->m_items )
2302  {
2303  switch( item->Type() )
2304  {
2305  case SCH_BUS_WIRE_ENTRY_T:
2306  {
2307  if( !bus_entry )
2308  bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2309  break;
2310  }
2311 
2312  default:
2313  break;
2314  }
2315  }
2316 
2317  if( bus_entry && bus_entry->m_connected_bus_item )
2318  {
2319  bus_wire = bus_entry->m_connected_bus_item;
2320 
2321  wxASSERT( bus_wire->Type() == SCH_LINE_T );
2322 
2323  // In some cases, the connection list (SCH_CONNECTION*) can be null.
2324  // Skip null connections.
2325  if( bus_entry->Connection( &sheet ) && bus_wire->Type() == SCH_LINE_T
2326  && bus_wire->Connection( &sheet ) )
2327  {
2328  conflict = true;
2329  bus_name = bus_wire->Connection( &sheet )->Name( true );
2330 
2331  auto test_name = bus_entry->Connection( &sheet )->Name( true );
2332 
2333  for( const auto& member : bus_wire->Connection( &sheet )->Members() )
2334  {
2335  if( member->Type() == CONNECTION_TYPE::BUS )
2336  {
2337  for( const auto& sub_member : member->Members() )
2338  {
2339  if( sub_member->Name( true ) == test_name )
2340  conflict = false;
2341  }
2342  }
2343  else if( member->Name( true ) == test_name )
2344  {
2345  conflict = false;
2346  }
2347  }
2348  }
2349  }
2350 
2351  // Don't report warnings if this bus member has been overridden by a higher priority power pin
2352  // or global label
2353  if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
2355  conflict = false;
2356 
2357  if( conflict )
2358  {
2359  wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
2360  " member of that bus" ),
2361  aSubgraph->m_driver_connection->Name( true ),
2362  bus_name );
2363  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
2364  ercItem->SetItems( bus_entry, bus_wire );
2365  ercItem->SetErrorMessage( msg );
2366 
2367  SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
2368  screen->Append( marker );
2369 
2370  return false;
2371  }
2372 
2373  return true;
2374 }
SCH_SHEET_PATH m_sheet
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
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:160
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:131
wxString Name(bool aIgnoreSheet=false) const
std::vector< SCH_ITEM * > m_items
A wire connected to a bus doesn't match the bus.
Definition: erc_settings.h:56
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
SCH_SCREEN * LastScreen()
#define _(s)
Definition: 3d_actions.cpp:33
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
PRIORITY GetDriverPriority()
This item represents a bus vector.
KICAD_T Type() const
Function Type()
Definition: eda_item.h:181
wxPoint GetPosition() const override

References _, BUS, SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_BUS_ENTRY_CONFLICT, Format(), 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_driver_connection, 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 2166 of file connection_graph.cpp.

2167 {
2168  auto sheet = aSubgraph->m_sheet;
2169  auto screen = sheet.LastScreen();
2170 
2171  SCH_ITEM* net_item = nullptr;
2172  SCH_ITEM* bus_item = nullptr;
2173  SCH_CONNECTION conn( this );
2174 
2175  for( auto item : aSubgraph->m_items )
2176  {
2177  switch( item->Type() )
2178  {
2179  case SCH_LINE_T:
2180  {
2181  if( item->GetLayer() == LAYER_BUS )
2182  bus_item = ( !bus_item ) ? item : bus_item;
2183  else
2184  net_item = ( !net_item ) ? item : net_item;
2185  break;
2186  }
2187 
2188  case SCH_GLOBAL_LABEL_T:
2189  case SCH_SHEET_PIN_T:
2190  case SCH_HIER_LABEL_T:
2191  {
2192  SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2193  conn.ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
2194 
2195  if( conn.IsBus() )
2196  bus_item = ( !bus_item ) ? item : bus_item;
2197  else
2198  net_item = ( !net_item ) ? item : net_item;
2199  break;
2200  }
2201 
2202  default:
2203  break;
2204  }
2205  }
2206 
2207  if( net_item && bus_item )
2208  {
2209  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
2210  ercItem->SetItems( net_item, bus_item );
2211 
2212  SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
2213  screen->Append( marker );
2214 
2215  return false;
2216  }
2217 
2218  return true;
2219 }
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:160
A bus wire is graphically connected to a net port/pin (or vice versa).
Definition: erc_settings.h:60
virtual wxPoint GetPosition() const
Definition: eda_item.h:325
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:492
SCH_SCREEN * 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 2550 of file connection_graph.cpp.

2551 {
2552  if( aSubgraph->m_driver )
2553  return true;
2554 
2555  std::vector<SCH_LINE*> wires;
2556 
2557  // We've gotten this far, so we know we have no valid driver. All we need to do is check
2558  // for a wire that we can place the error on.
2559 
2560  for( SCH_ITEM* item : aSubgraph->m_items )
2561  {
2562  if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
2563  wires.emplace_back( static_cast<SCH_LINE*>( item ) );
2564  }
2565 
2566  if( !wires.empty() )
2567  {
2568  SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
2569 
2570  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
2571  ercItem->SetItems( wires[0],
2572  wires.size() > 1 ? wires[1] : nullptr,
2573  wires.size() > 2 ? wires[2] : nullptr,
2574  wires.size() > 3 ? wires[3] : nullptr );
2575 
2576  SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
2577  screen->Append( marker );
2578 
2579  return false;
2580  }
2581 
2582  return true;
2583 }
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:160
Some wires are not connected to anything else.
Definition: erc_settings.h:64
std::vector< SCH_ITEM * > m_items
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:285
SCH_SCREEN * LastScreen()
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:129
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: eda_item.h:181

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 2702 of file connection_graph.cpp.

2703 {
2704  int errors = 0;
2705 
2706  for( const SCH_SHEET_PATH& sheet : m_sheetList )
2707  {
2708  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
2709  {
2710  if( item->Type() != SCH_SHEET_T )
2711  continue;
2712 
2713  SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
2714 
2715  std::map<wxString, SCH_SHEET_PIN*> pins;
2716 
2717  for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
2718  pins[pin->GetText()] = pin;
2719 
2720  for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
2721  {
2722  if( subItem->Type() == SCH_HIER_LABEL_T )
2723  {
2724  SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
2725  pins.erase( label->GetText() );
2726  }
2727  }
2728 
2729  for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
2730  {
2731  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
2732  ercItem->SetItems( unmatched.second );
2733  ercItem->SetErrorMessage( wxString::Format(
2734  _( "Sheet port %s has no matching hierarchical label inside the sheet" ),
2735  unmatched.first ) );
2736 
2737  SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
2738  sheet.LastScreen()->Append( marker );
2739 
2740  errors++;
2741  }
2742  }
2743  }
2744 
2745  return errors;
2746 }
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:160
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:284
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:85
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:219
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:364
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
Mismatch between hierarchical labels and pins sheets.
Definition: erc_settings.h:45
#define _(s)
Definition: 3d_actions.cpp:33
SCH_SHEET_LIST m_sheetList
EE_RTREE & Items()
Definition: sch_screen.h:159
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:133

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 2586 of file connection_graph.cpp.

2587 {
2588  // Label connection rules:
2589  // Local labels are flagged if they don't connect to any pins and don't have a no-connect
2590  // Global labels are flagged if they appear only once, don't connect to any local labels,
2591  // and don't have a no-connect marker
2592 
2593  // So, if there is a no-connect, we will never generate a warning here
2594  if( aSubgraph->m_no_connect )
2595  return true;
2596 
2597  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2598  bool ok = true;
2599  SCH_TEXT* text = nullptr;
2600  bool hasOtherConnections = false;
2601  int pinCount = 0;
2602 
2603  for( auto item : aSubgraph->m_items )
2604  {
2605  switch( item->Type() )
2606  {
2607  case SCH_LABEL_T:
2608  case SCH_GLOBAL_LABEL_T:
2609  case SCH_HIER_LABEL_T:
2610  {
2611  text = static_cast<SCH_TEXT*>( item );
2612 
2613  // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
2614  // we want to error if an individual label in the subgraph is floating, even if it's
2615  // connected to other valid things by way of another label on the same sheet.
2616 
2617  if( text->IsDangling() && settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
2618  {
2619  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
2620  ercItem->SetItems( text );
2621 
2622  SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
2623  aSubgraph->m_sheet.LastScreen()->Append( marker );
2624  ok = false;
2625  }
2626 
2627  break;
2628  }
2629 
2630  case SCH_PIN_T:
2631  case SCH_SHEET_PIN_T:
2632  hasOtherConnections = true;
2633  pinCount++;
2634  break;
2635 
2636  default:
2637  break;
2638  }
2639  }
2640 
2641  if( !text )
2642  return true;
2643 
2644  bool isGlobal = text->Type() == SCH_GLOBAL_LABEL_T;
2645  int errCode = isGlobal ? ERCE_GLOBLABEL : ERCE_LABEL_NOT_CONNECTED;
2646 
2647  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2648 
2649  wxString name = EscapeString( text->GetShownText(), CTX_NETNAME );
2650 
2651  if( isGlobal )
2652  {
2653  // This will be set to true if the global is connected to a pin above, but we
2654  // want to reset this to false so that globals get flagged if they only have a
2655  // single instance connected to a single pin
2656  hasOtherConnections = ( pinCount < 2 );
2657 
2658  auto it = m_net_name_to_subgraphs_map.find( name );
2659 
2660  if( it != m_net_name_to_subgraphs_map.end() )
2661  {
2662  if( it->second.size() > 1 || aSubgraph->m_multiple_drivers )
2663  hasOtherConnections = true;
2664  }
2665  }
2666  else if( text->Type() == SCH_HIER_LABEL_T )
2667  {
2668  // For a hier label, check if the parent pin is connected
2669  if( aSubgraph->m_hier_parent &&
2670  ( aSubgraph->m_hier_parent->m_strong_driver ||
2671  aSubgraph->m_hier_parent->m_drivers.size() > 1) )
2672  {
2673  // For now, a simple check: if there is more than one driver, the parent is probably
2674  // connected elsewhere (because at least one driver will be the hier pin itself)
2675  hasOtherConnections = true;
2676  }
2677  }
2678  else
2679  {
2680  auto pair = std::make_pair( aSubgraph->m_sheet, name );
2681  auto it = m_local_label_cache.find( pair );
2682 
2683  if( it != m_local_label_cache.end() && it->second.size() > 1 )
2684  hasOtherConnections = true;
2685  }
2686 
2687  if( !hasOtherConnections && settings.IsTestEnabled( errCode ) )
2688  {
2689  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
2690  ercItem->SetItems( text );
2691 
2692  SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
2693  aSubgraph->m_sheet.LastScreen()->Append( marker );
2694 
2695  return false;
2696  }
2697 
2698  return ok;
2699 }
bool IsDangling() const override
Definition: sch_text.h:303
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:160
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:114
Label not connected to anything.
Definition: erc_settings.h:48
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
Container for ERC settings.
Definition: erc_settings.h:97
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_text.cpp:492
wxPoint GetPosition() const override
Definition: sch_text.h:312
A global label is unique.
Definition: erc_settings.h:62
SCH_SCREEN * LastScreen()
const char * name
Definition: DXF_plotter.cpp:59
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:129
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:132
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: eda_item.h:181

References SCH_SCREEN::Append(), ERC_ITEM::Create(), CTX_NETNAME, ERCE_GLOBLABEL, ERCE_LABEL_NOT_CONNECTED, SCHEMATIC::ErcSettings(), EscapeString(), SCH_TEXT::GetPosition(), SCH_TEXT::GetShownText(), SCH_TEXT::IsDangling(), 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 2378 of file connection_graph.cpp.

2379 {
2380  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2381  wxString msg;
2382  const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2383  SCH_SCREEN* screen = sheet.LastScreen();
2384  bool ok = true;
2385 
2386  if( aSubgraph->m_no_connect != nullptr )
2387  {
2388  bool has_invalid_items = false;
2389  bool has_other_items = false;
2390  SCH_PIN* pin = nullptr;
2391  std::vector<SCH_ITEM*> invalid_items;
2392  wxPoint noConnectPos = aSubgraph->m_no_connect->GetPosition();
2393  double minDist = 0;
2394 
2395  // Any subgraph that contains both a pin and a no-connect should not
2396  // contain any other driving items.
2397 
2398  for( auto item : aSubgraph->m_items )
2399  {
2400  switch( item->Type() )
2401  {
2402  case SCH_PIN_T:
2403  {
2404  SCH_PIN* candidate = static_cast<SCH_PIN*>( item );
2405  double dist = VECTOR2I( candidate->GetTransformedPosition() - noConnectPos )
2406  .SquaredEuclideanNorm();
2407 
2408  if( !pin || dist < minDist )
2409  {
2410  pin = candidate;
2411  minDist = dist;
2412  }
2413 
2414  has_invalid_items |= has_other_items;
2415  has_other_items = true;
2416  break;
2417  }
2418 
2419  case SCH_LINE_T:
2420  case SCH_JUNCTION_T:
2421  case SCH_NO_CONNECT_T:
2422  break;
2423 
2424  default:
2425  has_invalid_items = true;
2426  has_other_items = true;
2427  invalid_items.push_back( item );
2428  }
2429  }
2430 
2431  if( pin && has_invalid_items && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
2432  {
2433  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
2434  ercItem->SetItems( pin );
2435 
2436  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2437  screen->Append( marker );
2438 
2439  ok = false;
2440  }
2441 
2442  if( !has_other_items && settings.IsTestEnabled(ERCE_NOCONNECT_NOT_CONNECTED ) )
2443  {
2444  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
2445  ercItem->SetItems( aSubgraph->m_no_connect );
2446 
2447  SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
2448  screen->Append( marker );
2449 
2450  ok = false;
2451  }
2452  }
2453  else
2454  {
2455  bool has_other_connections = false;
2456  std::vector<SCH_PIN*> pins;
2457 
2458  // Any subgraph that lacks a no-connect and contains a pin should also
2459  // contain at least one other potential driver
2460 
2461  for( SCH_ITEM* item : aSubgraph->m_items )
2462  {
2463  switch( item->Type() )
2464  {
2465  case SCH_PIN_T:
2466  {
2467  if( !pins.empty() )
2468  has_other_connections = true;
2469 
2470  pins.emplace_back( static_cast<SCH_PIN*>( item ) );
2471 
2472  break;
2473  }
2474 
2475  default:
2476  if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
2477  has_other_connections = true;
2478 
2479  break;
2480  }
2481  }
2482 
2483  // For many checks, we can just use the first pin
2484  SCH_PIN* pin = pins.empty() ? nullptr : pins[0];
2485 
2486  // Check if invisible power input pins connect to anything else via net name,
2487  // but not for power symbols as the ones in the standard library all have invisible pins
2488  // and we want to throw unconnected errors for those even if they are connected to other
2489  // net items by name, because usually failing to connect them graphically is a mistake
2490  if( pin && !has_other_connections
2492  && !pin->IsVisible()
2493  && !pin->GetLibPin()->GetParent()->IsPower() )
2494  {
2495  wxString name = pin->Connection( &sheet )->Name();
2496  wxString local_name = pin->Connection( &sheet )->Name( true );
2497 
2498  if( m_global_label_cache.count( name ) ||
2499  ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
2500  {
2501  has_other_connections = true;
2502  }
2503  }
2504 
2505  // Only one pin, and it's not a no-connect pin
2506  if( pin && !has_other_connections
2507  && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
2508  && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2509  {
2510  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2511  ercItem->SetItems( pin );
2512 
2513  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2514  screen->Append( marker );
2515 
2516  ok = false;
2517  }
2518 
2519  // If there are multiple pins in this SG, they might be indirectly connected (by netname)
2520  // rather than directly connected (by wires). We want to flag dangling pins even if they
2521  // join nets with another pin, as it's often a mistake
2522  if( pins.size() > 1 )
2523  {
2524  for( SCH_PIN* testPin : pins )
2525  {
2526  // We only apply this test to power symbols, because other symbols have invisible
2527  // pins that are meant to be dangling, but the KiCad standard library power symbols
2528  // have invisible pins that are *not* meant to be dangling.
2529  if( testPin->GetLibPin()->GetParent()->IsPower()
2530  && testPin->ConnectedItems( sheet ).empty()
2531  && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
2532  {
2533  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2534  ercItem->SetItems( testPin );
2535 
2536  SCH_MARKER* marker = new SCH_MARKER( ercItem,
2537  testPin->GetTransformedPosition() );
2538  screen->Append( marker );
2539 
2540  ok = false;
2541  }
2542  }
2543  }
2544  }
2545 
2546  return ok;
2547 }
power input (GND, VCC for ICs). Must be connected to a power output.
SCH_SHEET_PATH m_sheet
Pin not connected and not no connect symbol.
Definition: erc_settings.h:40
LIB_PIN * GetLibPin() const
Definition: sch_pin.h:69
bool IsVisible() const
Definition: sch_pin.h:112
LIB_PART * GetParent() const
Definition: lib_item.h:183
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:160
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:114
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
SCHEMATIC * m_schematic
The schematic this graph represents.
virtual wxPoint GetPosition() const
Definition: eda_item.h:325
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:131
wxString Name(bool aIgnoreSheet=false) const
std::vector< SCH_ITEM * > m_items
Container for ERC settings.
Definition: erc_settings.h:97
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
A no connect symbol is not connected to anything.
Definition: erc_settings.h:47
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
A no connect symbol is connected to more than 1 pin.
Definition: erc_settings.h:46
bool IsPower() const
SCH_SCREEN * LastScreen()
const char * name
Definition: DXF_plotter.cpp:59
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:129
ELECTRICAL_PINTYPE GetType() const
Definition: sch_pin.cpp:88
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:132
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: eda_item.h:181
wxPoint GetTransformedPosition() const
Returns the pin's position in global coordinates.
Definition: sch_pin.cpp:266

References SCH_SCREEN::Append(), SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_NOCONNECT_CONNECTED, ERCE_NOCONNECT_NOT_CONNECTED, ERCE_PIN_NOT_CONNECTED, SCHEMATIC::ErcSettings(), CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_PIN::GetLibPin(), LIB_ITEM::GetParent(), EDA_ITEM::GetPosition(), SCH_PIN::GetTransformedPosition(), SCH_PIN::GetType(), LIB_PART::IsPower(), ERC_SETTINGS::IsTestEnabled(), SCH_PIN::IsVisible(), SCH_SHEET_PATH::LastScreen(), m_global_label_cache, CONNECTION_SUBGRAPH::m_items, m_local_label_cache, CONNECTION_SUBGRAPH::m_no_connect, m_schematic, 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 2063 of file connection_graph.cpp.

2064 {
2065  auto it = m_net_name_to_subgraphs_map.find( aNetName );
2066 
2067  if( it == m_net_name_to_subgraphs_map.end() )
2068  return nullptr;
2069 
2070  wxASSERT( !it->second.empty() );
2071 
2072  return it->second[0];
2073 }
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 2042 of file connection_graph.cpp.

2044 {
2045  auto it = m_net_name_to_subgraphs_map.find( aNetName );
2046 
2047  if( it == m_net_name_to_subgraphs_map.end() )
2048  return nullptr;
2049 
2050  for( CONNECTION_SUBGRAPH* sg : it->second )
2051  {
2052  // Cache is supposed to be valid by now
2053  wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
2054 
2055  if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
2056  return sg;
2057  }
2058 
2059  return nullptr;
2060 }
SCH_SHEET_PATH m_sheet
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
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.
wxString Name(bool aIgnoreSheet=false) const

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

Referenced by NETLIST_EXPORTER_BASE::CreatePinList(), and NETLIST_EXPORTER_BASE::findAllUnitsOfSymbol().

◆ 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 1986 of file connection_graph.cpp.

1987 {
1988  auto it = m_bus_alias_cache.find( aName );
1989 
1990  return it != m_bus_alias_cache.end() ? it->second : nullptr;
1991 }
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 1994 of file connection_graph.cpp.

1995 {
1996  std::vector<const CONNECTION_SUBGRAPH*> ret;
1997 
1998  for( auto&& subgraph : m_subgraphs )
1999  {
2000  // Graph is supposed to be up-to-date before calling this
2001  wxASSERT( !subgraph->m_dirty );
2002 
2003  if( !subgraph->m_driver )
2004  continue;
2005 
2006  auto sheet = subgraph->m_sheet;
2007  auto connection = subgraph->m_driver->Connection( &sheet );
2008 
2009  if( !connection->IsBus() )
2010  continue;
2011 
2012  auto labels = subgraph->GetBusLabels();
2013 
2014  if( labels.size() > 1 )
2015  {
2016  bool different = false;
2017  wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
2018 
2019  for( unsigned i = 1; i < labels.size(); ++i )
2020  {
2021  if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
2022  {
2023  different = true;
2024  break;
2025  }
2026  }
2027 
2028  if( !different )
2029  continue;
2030 
2031  wxLogTrace( ConnTrace, "SG %ld (%s) has multiple bus labels", subgraph->m_code,
2032  connection->Name() );
2033 
2034  ret.push_back( subgraph );
2035  }
2036  }
2037 
2038  return ret;
2039 }
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
static const wxChar ConnTrace[]

References ConnTrace, and 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 1877 of file connection_graph.cpp.

1879 {
1880  auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
1881 
1882  switch( aItem->Type() )
1883  {
1884  case SCH_PIN_T:
1885  {
1886  auto pin = static_cast<SCH_PIN*>( aItem );
1887 
1888  if( pin->IsPowerConnection() )
1889  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1890 
1891  break;
1892  }
1893 
1894  case SCH_GLOBAL_LABEL_T:
1895  case SCH_HIER_LABEL_T:
1896  case SCH_LABEL_T:
1897  {
1898  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1899  break;
1900  }
1901 
1902  default:
1903  break;
1904  }
1905 
1906  if( c )
1907  {
1908  c->SetGraph( this );
1909  c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
1910  }
1911 
1912  return c;
1913 }
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: eda_item.h:181

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 2076 of file connection_graph.cpp.

2077 {
2078  auto it = m_item_to_subgraph_map.find( aItem );
2079  CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
2080 
2081  while( ret && ret->m_absorbed )
2082  ret = ret->m_absorbed_by;
2083 
2084  return ret;
2085 }
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 inheritNetclass(), and 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 1916 of file connection_graph.cpp.

1918 {
1919  wxASSERT( aBusConnection->IsBus() );
1920 
1921  SCH_CONNECTION* match = nullptr;
1922 
1923  if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
1924  {
1925  // Vector bus: compare against index, because we allow the name
1926  // to be different
1927 
1928  for( const auto& bus_member : aBusConnection->Members() )
1929  {
1930  if( bus_member->VectorIndex() == aSearch->VectorIndex() )
1931  {
1932  match = bus_member.get();
1933  break;
1934  }
1935  }
1936  }
1937  else
1938  {
1939  // Group bus
1940  for( const auto& c : aBusConnection->Members() )
1941  {
1942  // Vector inside group: compare names, because for bus groups
1943  // we expect the naming to be consistent across all usages
1944  // TODO(JE) explain this in the docs
1945  if( c->Type() == CONNECTION_TYPE::BUS )
1946  {
1947  for( const auto& bus_member : c->Members() )
1948  {
1949  if( bus_member->LocalName() == aSearch->LocalName() )
1950  {
1951  match = bus_member.get();
1952  break;
1953  }
1954  }
1955  }
1956  else if( c->LocalName() == aSearch->LocalName() )
1957  {
1958  match = c.get();
1959  break;
1960  }
1961  }
1962  }
1963 
1964  return match;
1965 }
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 1596 of file connection_graph.cpp.

1597 {
1598  SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
1599  std::vector<CONNECTION_SUBGRAPH*> search_list;
1600  std::unordered_set<CONNECTION_SUBGRAPH*> visited;
1601  std::vector<SCH_CONNECTION*> stale_bus_members;
1602 
1603  auto visit =
1604  [&]( CONNECTION_SUBGRAPH* aParent )
1605  {
1606  for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
1607  {
1608  SCH_SHEET_PATH path = aParent->m_sheet;
1609  path.push_back( pin->GetParent() );
1610 
1611  auto it = m_sheet_to_subgraphs_map.find( path );
1612 
1613  if( it == m_sheet_to_subgraphs_map.end() )
1614  continue;
1615 
1616  for( CONNECTION_SUBGRAPH* candidate : it->second )
1617  {
1618  if( !candidate->m_strong_driver
1619  || candidate->m_hier_ports.empty()
1620  || visited.count( candidate ) )
1621  {
1622  continue;
1623  }
1624 
1625  for( SCH_HIERLABEL* label : candidate->m_hier_ports )
1626  {
1627  if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
1628  {
1629  wxLogTrace( ConnTrace, "%lu: found child %lu (%s)", aParent->m_code,
1630  candidate->m_code, candidate->m_driver_connection->Name() );
1631 
1632  candidate->m_hier_parent = aParent;
1633 
1634  search_list.push_back( candidate );
1635  break;
1636  }
1637  }
1638  }
1639  }
1640 
1641  for( SCH_HIERLABEL* label : aParent->m_hier_ports )
1642  {
1643  SCH_SHEET_PATH path = aParent->m_sheet;
1644  path.pop_back();
1645 
1646  auto it = m_sheet_to_subgraphs_map.find( path );
1647 
1648  if( it == m_sheet_to_subgraphs_map.end() )
1649  continue;
1650 
1651  for( CONNECTION_SUBGRAPH* candidate : it->second )
1652  {
1653  if( candidate->m_hier_pins.empty()
1654  || visited.count( candidate )
1655  || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
1656  {
1657  continue;
1658  }
1659 
1660  for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
1661  {
1662  SCH_SHEET_PATH pin_path = path;
1663  pin_path.push_back( pin->GetParent() );
1664 
1665  if( pin_path != aParent->m_sheet )
1666  continue;
1667 
1668  if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
1669  {
1670  wxLogTrace( ConnTrace, "%lu: found additional parent %lu (%s)",
1671  aParent->m_code, candidate->m_code,
1672  candidate->m_driver_connection->Name() );
1673 
1674  search_list.push_back( candidate );
1675  break;
1676  }
1677  }
1678  }
1679  }
1680  };
1681 
1682  auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
1683  for( const auto& kv : aParentGraph->m_bus_neighbors )
1684  {
1685  for( CONNECTION_SUBGRAPH* neighbor : kv.second )
1686  {
1687  // May have been absorbed but won't have been deleted
1688  while( neighbor->m_absorbed )
1689  neighbor = neighbor->m_absorbed_by;
1690 
1691  SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
1692 
1693  // Now member may be out of date, since we just cloned the
1694  // connection from higher up in the hierarchy. We need to
1695  // figure out what the actual new connection is.
1696  SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
1697 
1698  if( !member )
1699  {
1700  // Try harder: we might match on a secondary driver
1701  for( CONNECTION_SUBGRAPH* sg : kv.second )
1702  {
1703  if( sg->m_multiple_drivers )
1704  {
1705  SCH_SHEET_PATH sheet = sg->m_sheet;
1706 
1707  for( SCH_ITEM* driver : sg->m_drivers )
1708  {
1709  auto c = getDefaultConnection( driver, sg );
1710  member = matchBusMember( parent, c.get() );
1711 
1712  if( member )
1713  break;
1714  }
1715  }
1716 
1717  if( member )
1718  break;
1719  }
1720  }
1721 
1722  // This is bad, probably an ERC error
1723  if( !member )
1724  {
1725  wxLogTrace( ConnTrace, "Could not match bus member %s in %s",
1726  kv.first->Name(), parent->Name() );
1727  continue;
1728  }
1729 
1730  auto neighbor_conn = neighbor->m_driver_connection;
1731  auto neighbor_name = neighbor_conn->Name();
1732 
1733  // Matching name: no update needed
1734  if( neighbor_name == member->Name() )
1735  continue;
1736 
1737  // Safety check against infinite recursion
1738  wxASSERT( neighbor_conn->IsNet() );
1739 
1740  wxLogTrace( ConnTrace, "%lu (%s) connected to bus member %s (local %s)",
1741  neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
1742 
1743  // Take whichever name is higher priority
1746  {
1747  member->Clone( *neighbor_conn );
1748  stale_bus_members.push_back( member );
1749  }
1750  else
1751  {
1752  neighbor_conn->Clone( *member );
1753  neighbor->UpdateItemConnections();
1754 
1755  recacheSubgraphName( neighbor, neighbor_name );
1756 
1757  // Recurse onto this neighbor in case it needs to re-propagate
1758  neighbor->m_dirty = true;
1759  propagateToNeighbors( neighbor );
1760  }
1761  }
1762  }
1763  };
1764 
1765  // If we are a bus, we must propagate to local neighbors and then the hierarchy
1766  if( conn->IsBus() )
1767  propagate_bus_neighbors( aSubgraph );
1768 
1769  // If we don't have any hier pins (i.e. no children), nothing to do
1770  if( aSubgraph->m_hier_pins.empty() )
1771  {
1772  // If we also don't have any parents, we'll never be visited again
1773  if( aSubgraph->m_hier_ports.empty() )
1774  aSubgraph->m_dirty = false;
1775 
1776  return;
1777  }
1778 
1779  // If we do have hier ports, skip this subgraph as it will be visited by a parent
1780  // TODO(JE) this will leave the subgraph dirty if there is no matching parent subgraph,
1781  // which should be flagged as an ERC error
1782  if( !aSubgraph->m_hier_ports.empty() )
1783  return;
1784 
1785  visited.insert( aSubgraph );
1786 
1787  wxLogTrace( ConnTrace, "Propagating %lu (%s) to subsheets",
1788  aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
1789 
1790  visit( aSubgraph );
1791 
1792  for( unsigned i = 0; i < search_list.size(); i++ )
1793  {
1794  auto child = search_list[i];
1795 
1796  visited.insert( child );
1797 
1798  visit( child );
1799 
1800  child->m_dirty = false;
1801  }
1802 
1803  // Now, find the best driver for this chain of subgraphs
1804  CONNECTION_SUBGRAPH* driver = aSubgraph;
1807 
1808  // Check if a subsheet has a higher-priority connection to the same net
1810  {
1811  for( CONNECTION_SUBGRAPH* subgraph : visited )
1812  {
1814  CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
1815 
1816  // Upgrade driver to be this subgraph if this subgraph has a power pin or global
1817  // Also upgrade if we found something with a shorter sheet path (higher in hierarchy)
1818  // but with an equivalent priority
1819 
1820  if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
1821  ( priority >= highest && subgraph->m_sheet.size() < aSubgraph->m_sheet.size() ) )
1822  driver = subgraph;
1823  }
1824  }
1825 
1826  if( driver != aSubgraph )
1827  {
1828  wxLogTrace( ConnTrace, "%lu (%s) overridden by new driver %lu (%s)",
1829  aSubgraph->m_code, aSubgraph->m_driver_connection->Name(),
1830  driver->m_code, driver->m_driver_connection->Name() );
1831  }
1832 
1833  conn = driver->m_driver_connection;
1834 
1835  for( CONNECTION_SUBGRAPH* subgraph : visited )
1836  {
1837  wxString old_name = subgraph->m_driver_connection->Name();
1838 
1839  subgraph->m_driver_connection->Clone( *conn );
1840  subgraph->UpdateItemConnections();
1841 
1842  if( old_name != conn->Name() )
1843  recacheSubgraphName( subgraph, old_name );
1844 
1845  if( conn->IsBus() )
1846  propagate_bus_neighbors( subgraph );
1847  }
1848 
1849  // Somewhere along the way, a bus member may have been upgraded to a global or power label.
1850  // Because this can happen anywhere, we need a second pass to update all instances of that bus
1851  // member to have the correct connection info
1852  if( conn->IsBus() && !stale_bus_members.empty() )
1853  {
1854  for( auto stale_member : stale_bus_members )
1855  {
1856  for( CONNECTION_SUBGRAPH* subgraph : visited )
1857  {
1858  SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
1859  stale_member );
1860  wxASSERT( member );
1861 
1862  wxLogTrace( ConnTrace, "Updating %lu (%s) member %s to %s", subgraph->m_code,
1863  subgraph->m_driver_connection->Name(), member->LocalName(),
1864  stale_member->Name() );
1865 
1866  member->Clone( *stale_member );
1867 
1868  propagate_bus_neighbors( subgraph );
1869  }
1870  }
1871  }
1872 
1873  aSubgraph->m_dirty = false;
1874 }
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:169
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
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.
static const wxChar ConnTrace[]
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
const wxString & GetNameForDriver(SCH_ITEM *aItem)
Returns the candidate net name for a driver.
size_t size() const
Forwarded method from std::vector.
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:85
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
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.
CONNECTION_SUBGRAPH * m_hier_parent
bool IsBus() const
CONNECTION_TYPE Type() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
PRIORITY GetDriverPriority()
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(), ConnTrace, getDefaultConnection(), CONNECTION_SUBGRAPH::GetDriverPriority(), CONNECTION_SUBGRAPH::GetNameForDriver(), 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_parent, CONNECTION_SUBGRAPH::m_hier_pins, CONNECTION_SUBGRAPH::m_hier_ports, CONNECTION_SUBGRAPH::m_multiple_drivers, CONNECTION_SUBGRAPH::m_sheet, m_sheet_to_subgraphs_map, CONNECTION_SUBGRAPH::m_strong_driver, matchBusMember(), SCH_CONNECTION::Name(), SCH_SHEET_PATH::pop_back(), CONNECTION_SUBGRAPH::POWER_PIN, SCH_SHEET_PATH::push_back(), recacheSubgraphName(), SCH_SHEET_PATH::size(), SCH_CONNECTION::Type(), and CONNECTION_SUBGRAPH::UpdateItemConnections().

Referenced by buildConnectionGraph().

◆ recacheSubgraphName()

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

Definition at line 1968 of file connection_graph.cpp.

1970 {
1971  auto it = m_net_name_to_subgraphs_map.find( aOldName );
1972 
1973  if( it != m_net_name_to_subgraphs_map.end() )
1974  {
1975  std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
1976  vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
1977  }
1978 
1979  wxLogTrace( ConnTrace, "recacheSubgraphName: %s => %s", aOldName,
1980  aSubgraph->m_driver_connection->Name() );
1981 
1982  m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
1983 }
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
static const wxChar ConnTrace[]
wxString Name(bool aIgnoreSheet=false) const

References ConnTrace, 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 427 of file connection_graph.cpp.

428 {
429  PROF_COUNTER recalc_time( "CONNECTION_GRAPH::Recalculate" );
430 
431  if( aUnconditional )
432  Reset();
433 
434  PROF_COUNTER update_items( "updateItemConnectivity" );
435 
436  m_sheetList = aSheetList;
437 
438  for( const SCH_SHEET_PATH& sheet : aSheetList )
439  {
440  std::vector<SCH_ITEM*> items;
441 
442  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
443  {
444  if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
445  items.push_back( item );
446  }
447 
448  m_items.reserve( m_items.size() + items.size() );
449 
450  updateItemConnectivity( sheet, items );
451 
452  // UpdateDanglingState() also adds connected items for SCH_TEXT
453  sheet.LastScreen()->TestDanglingEnds( &sheet );
454  }
455 
456  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
457  update_items.Show();
458 
459  PROF_COUNTER build_graph( "buildConnectionGraph" );
460 
462 
463  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
464  build_graph.Show();
465 
466  recalc_time.Stop();
467 
468  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
469  recalc_time.Show();
470 
471 #ifndef DEBUG
472  // Pressure relief valve for release builds
473  const double max_recalc_time_msecs = 250.;
474 
475  if( m_allowRealTime && ADVANCED_CFG::GetCfg().m_RealTimeConnectivity &&
476  recalc_time.msecs() > max_recalc_time_msecs )
477  {
478  m_allowRealTime = false;
479  }
480 #endif
481 }
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.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
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.
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 403 of file connection_graph.cpp.

404 {
405  for( auto& subgraph : m_subgraphs )
406  delete subgraph;
407 
408  m_items.clear();
409  m_subgraphs.clear();
410  m_driver_subgraphs.clear();
411  m_sheet_to_subgraphs_map.clear();
412  m_invisible_power_pins.clear();
413  m_bus_alias_cache.clear();
414  m_net_name_to_code_map.clear();
415  m_bus_name_to_code_map.clear();
418  m_item_to_subgraph_map.clear();
419  m_local_label_cache.clear();
420  m_global_label_cache.clear();
421  m_last_net_code = 1;
422  m_last_bus_code = 1;
424 }
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 2088 of file connection_graph.cpp.

2089 {
2090  int error_count = 0;
2091 
2092  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2093 
2094  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2095 
2096  for( auto&& subgraph : m_subgraphs )
2097  {
2098  // Graph is supposed to be up-to-date before calling RunERC()
2099  wxASSERT( !subgraph->m_dirty );
2100 
2112  if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
2113  {
2114  if( !subgraph->ResolveDrivers( true ) )
2115  error_count++;
2116  }
2117  else
2118  subgraph->ResolveDrivers( false );
2119 
2120  if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
2121  {
2122  if( !ercCheckBusToNetConflicts( subgraph ) )
2123  error_count++;
2124  }
2125 
2126  if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
2127  {
2128  if( !ercCheckBusToBusEntryConflicts( subgraph ) )
2129  error_count++;
2130  }
2131 
2132  if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
2133  {
2134  if( !ercCheckBusToBusConflicts( subgraph ) )
2135  error_count++;
2136  }
2137 
2138  if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
2139  {
2140  if( !ercCheckFloatingWires( subgraph ) )
2141  error_count++;
2142  }
2143 
2144  // The following checks are always performed since they don't currently
2145  // have an option exposed to the user
2146 
2147  if( !ercCheckNoConnects( subgraph ) )
2148  error_count++;
2149 
2150  if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
2151  || settings.IsTestEnabled( ERCE_GLOBLABEL ) )
2152  {
2153  if( !ercCheckLabels( subgraph ) )
2154  error_count++;
2155  }
2156  }
2157 
2158  // Hierarchical sheet checking is done at the schematic level
2159  if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
2160  error_count += ercCheckHierSheets();
2161 
2162  return error_count;
2163 }
bool ercCheckBusToBusEntryConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting bus entry to bus connections.
A connection between bus objects doesn't share at least one net.
Definition: erc_settings.h:58
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:114
A bus wire is graphically connected to a net port/pin (or vice versa).
Definition: erc_settings.h:60
bool ercCheckFloatingWires(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for floating wires.
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
Some wires are not connected to anything else.
Definition: erc_settings.h:64
Label not connected to anything.
Definition: erc_settings.h:48
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:97
A wire connected to a bus doesn't match the bus.
Definition: erc_settings.h:56
A global label is unique.
Definition: erc_settings.h:62
Mismatch between hierarchical labels and pins sheets.
Definition: erc_settings.h:45
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:132
Conflicting drivers (labels, etc) on a subgraph.
Definition: erc_settings.h:55
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 247 of file connection_graph.h.

248  {
249  m_schematic = aSchematic;
250  }
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 484 of file connection_graph.cpp.

486 {
487  std::map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
488 
489  for( SCH_ITEM* item : aItemList )
490  {
491  std::vector< wxPoint > points = item->GetConnectionPoints();
492  item->ConnectedItems( aSheet ).clear();
493 
494  if( item->Type() == SCH_SHEET_T )
495  {
496  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
497  {
498  if( !pin->Connection( &aSheet ) )
499  pin->InitializeConnection( aSheet, this );
500 
501  pin->ConnectedItems( aSheet ).clear();
502  pin->Connection( &aSheet )->Reset();
503 
504  connection_map[ pin->GetTextPos() ].push_back( pin );
505  m_items.emplace_back( pin );
506  }
507  }
508  else if( item->Type() == SCH_COMPONENT_T )
509  {
510  SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
511 
512  // TODO(JE) right now this relies on GetPins() returning good SCH_PIN pointers
513  // that contain good LIB_PIN pointers. Since these get invalidated whenever the
514  // library component is refreshed, the current solution as of ed025972 is to just
515  // rebuild the SCH_PIN list when the component is refreshed, and then re-run the
516  // connectivity calculations. This is slow and should be improved before release.
517  // See https://gitlab.com/kicad/code/kicad/issues/3784
518 
519  for( SCH_PIN* pin : component->GetPins( &aSheet ) )
520  {
521  pin->InitializeConnection( aSheet, this );
522 
523  wxPoint pos = pin->GetPosition();
524 
525  // because calling the first time is not thread-safe
526  pin->GetDefaultNetName( aSheet );
527  pin->ConnectedItems( aSheet ).clear();
528 
529  // Invisible power pins need to be post-processed later
530 
531  if( pin->IsPowerConnection() && !pin->IsVisible() )
532  m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
533 
534  connection_map[ pos ].push_back( pin );
535  m_items.emplace_back( pin );
536  }
537  }
538  else
539  {
540  m_items.emplace_back( item );
541  auto conn = item->InitializeConnection( aSheet, this );
542 
543  // Set bus/net property here so that the propagation code uses it
544  switch( item->Type() )
545  {
546  case SCH_LINE_T:
547  conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
549  break;
550 
551  case SCH_BUS_BUS_ENTRY_T:
552  conn->SetType( CONNECTION_TYPE::BUS );
553  // clean previous (old) links:
554  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
555  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
556  break;
557 
558  case SCH_PIN_T:
559  conn->SetType( CONNECTION_TYPE::NET );
560  break;
561 
563  conn->SetType( CONNECTION_TYPE::NET );
564  // clean previous (old) link:
565  static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
566  break;
567 
568  default:
569  break;
570  }
571 
572  for( const wxPoint& point : points )
573  connection_map[ point ].push_back( item );
574  }
575 
576  item->SetConnectivityDirty( false );
577  }
578 
579  for( const auto& it : connection_map )
580  {
581  auto connection_vec = it.second;
582 
583  for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
584  {
585  SCH_ITEM* connected_item = *primary_it;
586 
587  // Bus entries are special: they can have connection points in the
588  // middle of a wire segment, because the junction algo doesn't split
589  // the segment in two where you place a bus entry. This means that
590  // bus entries that don't land on the end of a line segment need to
591  // have "virtual" connection points to the segments they graphically
592  // touch.
593  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
594  {
595  // If this location only has the connection point of the bus
596  // entry itself, this means that either the bus entry is not
597  // connected to anything graphically, or that it is connected to
598  // a segment at some point other than at one of the endpoints.
599  if( connection_vec.size() == 1 )
600  {
601  SCH_SCREEN* screen = aSheet.LastScreen();
602  SCH_LINE* bus = screen->GetBus( it.first );
603 
604  if( bus )
605  {
606  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
607  bus_entry->m_connected_bus_item = bus;
608  }
609  }
610  }
611 
612  // Bus-to-bus entries are treated just like bus wires
613  else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
614  {
615  if( connection_vec.size() < 2 )
616  {
617  SCH_SCREEN* screen = aSheet.LastScreen();
618  SCH_LINE* bus = screen->GetBus( it.first );
619 
620  if( bus )
621  {
622  auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
623 
624  if( it.first == bus_entry->GetPosition() )
625  bus_entry->m_connected_bus_items[0] = bus;
626  else
627  bus_entry->m_connected_bus_items[1] = bus;
628 
629  bus_entry->ConnectedItems( aSheet ).insert( bus );
630  bus->ConnectedItems( aSheet ).insert( bus_entry );
631  }
632  }
633  }
634 
635  // Change junctions to be on bus junction layer if they are touching a bus
636  else if( connected_item->Type() == SCH_JUNCTION_T )
637  {
638  SCH_SCREEN* screen = aSheet.LastScreen();
639  SCH_LINE* bus = screen->GetBus( it.first );
640 
641  connected_item->SetLayer( bus ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
642  }
643 
644  for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
645  {
646  auto test_item = *test_it;
647 
648  if( connected_item != test_item &&
649  connected_item->ConnectionPropagatesTo( test_item ) &&
650  test_item->ConnectionPropagatesTo( connected_item ) )
651  {
652  connected_item->ConnectedItems( aSheet ).insert( test_item );
653  test_item->ConnectedItems( aSheet ).insert( connected_item );
654  }
655 
656  // Set up the link between the bus entry net and the bus
657  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
658  {
659  if( test_item->Connection( &aSheet )->IsBus() )
660  {
661  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
662  bus_entry->m_connected_bus_item = test_item;
663  }
664  }
665  }
666 
667  // If we got this far and did not find a connected bus item for a bus entry,
668  // we should do a manual scan in case there is a bus item on this connection
669  // point but we didn't pick it up earlier because there is *also* a net item here.
670  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
671  {
672  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
673 
674  if( !bus_entry->m_connected_bus_item )
675  {
676  auto screen = aSheet.LastScreen();
677  auto bus = screen->GetBus( it.first );
678 
679  if( bus )
680  bus_entry->m_connected_bus_item = bus;
681  }
682  }
683  }
684  }
685 }
virtual bool ConnectionPropagatesTo(const EDA_ITEM *aItem) const
Returns true if this item should propagate connection info to aItem.
Definition: sch_item.h:442
std::vector< SCH_ITEM * > m_items
void SetLayer(SCH_LAYER_ID aLayer)
Set the layer this item is on.
Definition: sch_item.h:292
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:85
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()
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:164
Schematic symbol object.
Definition: sch_component.h:79
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: eda_item.h:181
SCH_LINE * GetBus(const wxPoint &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T)
Definition: sch_screen.h:440

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 328 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 332 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 321 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 334 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 326 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 341 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 347 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 345 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 349 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 343 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 330 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 351 of file connection_graph.h.

Referenced by buildConnectionGraph(), ercCheckLabels(), ercCheckNoConnects(), 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 324 of file connection_graph.h.

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

◆ m_sheetList

SCH_SHEET_LIST CONNECTION_GRAPH::m_sheetList
private

Definition at line 312 of file connection_graph.h.

Referenced by ercCheckHierSheets(), and Recalculate().

◆ m_subgraphs

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

Definition at line 318 of file connection_graph.h.

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


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