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 (SCH_EDIT_FRAME *aFrame)
 
 ~CONNECTION_GRAPH ()
 
void Reset ()
 
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
 

Static Public Attributes

static bool m_allowRealTime = true
 

Private Member Functions

void updateItemConnectivity (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, SCH_SHEET_PATH aSheet)
 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 ercCheckLabels (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for proper connection of labels. 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

std::unordered_set< 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
 
NET_MAP m_net_code_to_subgraphs_map
 
int m_last_net_code
 
int m_last_bus_code
 
int m_last_subgraph_code
 
std::mutex m_item_mutex
 
SCH_EDIT_FRAMEm_frame
 

Detailed Description

Calculates the connectivity of a schematic and generates netlists.

Definition at line 210 of file connection_graph.h.

Constructor & Destructor Documentation

◆ CONNECTION_GRAPH()

CONNECTION_GRAPH::CONNECTION_GRAPH ( SCH_EDIT_FRAME aFrame)
inline

Definition at line 213 of file connection_graph.h.

214  : m_last_net_code( 1 ),
215  m_last_bus_code( 1 ),
217  m_frame( aFrame )
218  {}
SCH_EDIT_FRAME * m_frame

◆ ~CONNECTION_GRAPH()

CONNECTION_GRAPH::~CONNECTION_GRAPH ( )
inline

Definition at line 220 of file connection_graph.h.

221  {
222  Reset();
223  }

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

1420 {
1421  auto connections_to_check( aConnection->Members() );
1422 
1423  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1424  {
1425  auto member = connections_to_check[i];
1426 
1427  if( member->IsBus() )
1428  {
1429  connections_to_check.insert( connections_to_check.end(),
1430  member->Members().begin(),
1431  member->Members().end() );
1432  continue;
1433  }
1434 
1435  assignNewNetCode( *member );
1436  }
1437 }
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 1399 of file connection_graph.cpp.

1400 {
1401  int code;
1402 
1403  if( m_net_name_to_code_map.count( aConnection.Name() ) )
1404  {
1405  code = m_net_name_to_code_map.at( aConnection.Name() );
1406  }
1407  else
1408  {
1409  code = m_last_net_code++;
1410  m_net_name_to_code_map[ aConnection.Name() ] = code;
1411  }
1412 
1413  aConnection.SetNetCode( code );
1414 
1415  return code;
1416 }
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 624 of file connection_graph.cpp.

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

References CONNECTION_SUBGRAPH::AddItem(), assignNetCodesToBus(), assignNewNetCode(), SCH_CONNECTION::ClearDirty(), SCH_CONNECTION::Clone(), SCH_CONNECTION::ConfigureFromLabel(), SCH_ITEM::ConnectedItems(), SCH_ITEM::Connection(), Format(), g_RootSheet, getDefaultConnection(), CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_PIN::GetLibPin(), SCH_PIN::GetName(), CONNECTION_SUBGRAPH::GetNetName(), LIB_ITEM::GetParent(), EDA_ITEM::GetSelectMenuText(), 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_frame, m_global_label_cache, m_invisible_power_pins, 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, CONNECTION_SUBGRAPH::m_sheet, m_sheet_to_subgraphs_map, m_subgraphs, matchBusMember(), MILLIMETRES, name, SCH_CONNECTION::Name(), CONNECTION_SUBGRAPH::POWER_PIN, propagateToNeighbors(), PT_NC, 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::SetSuffix(), SCH_CONNECTION::SubgraphCode(), and EDA_ITEM::Type().

Referenced by Recalculate().

◆ ercCheckBusToBusConflicts()

bool CONNECTION_GRAPH::ercCheckBusToBusConflicts ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for conflicting connections between two bus items.

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

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

Definition at line 1978 of file connection_graph.cpp.

1979 {
1980  wxString msg;
1981  auto sheet = aSubgraph->m_sheet;
1982  auto screen = sheet.LastScreen();
1983 
1984  SCH_ITEM* label = nullptr;
1985  SCH_ITEM* port = nullptr;
1986 
1987  for( auto item : aSubgraph->m_items )
1988  {
1989  switch( item->Type() )
1990  {
1991  case SCH_TEXT_T:
1992  case SCH_GLOBAL_LABEL_T:
1993  {
1994  if( !label && item->Connection( sheet )->IsBus() )
1995  label = item;
1996  break;
1997  }
1998 
1999  case SCH_SHEET_PIN_T:
2000  case SCH_HIER_LABEL_T:
2001  {
2002  if( !port && item->Connection( sheet )->IsBus() )
2003  port = item;
2004  break;
2005  }
2006 
2007  default:
2008  break;
2009  }
2010  }
2011 
2012  if( label && port )
2013  {
2014  bool match = false;
2015 
2016  for( const auto& member : label->Connection( sheet )->Members() )
2017  {
2018  for( const auto& test : port->Connection( sheet )->Members() )
2019  {
2020  if( test != member && member->Name() == test->Name() )
2021  {
2022  match = true;
2023  break;
2024  }
2025  }
2026 
2027  if( match )
2028  break;
2029  }
2030 
2031  if( !match )
2032  {
2033  auto marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
2034  marker->SetData( m_frame->GetUserUnits(), ERCE_BUS_TO_BUS_CONFLICT,
2035  label->GetPosition(), label, port );
2036  screen->Append( marker );
2037 
2038  return false;
2039  }
2040  }
2041 
2042  return true;
2043 }
SCH_SHEET_PATH m_sheet
virtual wxPoint GetPosition() const =0
Function GetPosition.
std::vector< SCH_ITEM * > m_items
SCH_EDIT_FRAME * m_frame
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieves the connection associated with this object in the given sheet Note: the returned value can ...
Definition: sch_item.cpp:128
SCH_SCREEN * LastScreen()
Function LastScreen.
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
EDA_UNITS GetUserUnits() const
Return the user units currently in use.

References SCH_ITEM::Connection(), ERCE_BUS_TO_BUS_CONFLICT, SCH_ITEM::GetPosition(), EDA_BASE_FRAME::GetUserUnits(), SCH_CONNECTION::IsBus(), SCH_SHEET_PATH::LastScreen(), m_frame, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, MARKER_BASE::MARKER_ERC, 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 2046 of file connection_graph.cpp.

2047 {
2048  bool conflict = false;
2049  auto sheet = aSubgraph->m_sheet;
2050  auto screen = sheet.LastScreen();
2051 
2052  SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2053  SCH_ITEM* bus_wire = nullptr;
2054 
2055  for( auto item : aSubgraph->m_items )
2056  {
2057  switch( item->Type() )
2058  {
2059  case SCH_BUS_WIRE_ENTRY_T:
2060  {
2061  if( !bus_entry )
2062  bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2063  break;
2064  }
2065 
2066  default:
2067  break;
2068  }
2069  }
2070 
2071  if( bus_entry && bus_entry->m_connected_bus_item )
2072  {
2073  bus_wire = bus_entry->m_connected_bus_item;
2074 
2075  wxASSERT( bus_wire->Type() == SCH_LINE_T );
2076 
2077  // In some cases, the connection list (SCH_CONNECTION*) can be null.
2078  // Skip null connections.
2079  if( bus_entry->Connection( sheet ) && bus_wire->Type() == SCH_LINE_T
2080  && bus_wire->Connection( sheet ) )
2081  {
2082  conflict = true;
2083 
2084  auto test_name = bus_entry->Connection( sheet )->Name( true );
2085 
2086  for( const auto& member : bus_wire->Connection( sheet )->Members() )
2087  {
2088  if( member->Type() == CONNECTION_TYPE::BUS )
2089  {
2090  for( const auto& sub_member : member->Members() )
2091  {
2092  if( sub_member->Name( true ) == test_name )
2093  conflict = false;
2094  }
2095  }
2096  else if( member->Name( true ) == test_name )
2097  {
2098  conflict = false;
2099  }
2100  }
2101  }
2102  }
2103 
2104  // Don't report warnings if this bus member has been overridden by a higher priority power pin
2105  // or global label
2106  if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
2108  conflict = false;
2109 
2110  if( conflict )
2111  {
2112  auto marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
2113  marker->SetData( m_frame->GetUserUnits(), ERCE_BUS_ENTRY_CONFLICT,
2114  bus_entry->GetPosition(), bus_entry, bus_wire );
2115  screen->Append( marker );
2116 
2117  return false;
2118  }
2119 
2120  return true;
2121 }
SCH_SHEET_PATH m_sheet
SCH_ITEM * m_connected_bus_item
Pointer to the bus item (usually a bus wire) connected to this bus-wire entry, if it is connected to ...
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
wxString Name(bool aIgnoreSheet=false) const
std::vector< SCH_ITEM * > m_items
SCH_EDIT_FRAME * m_frame
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieves the connection associated with this object in the given sheet Note: the returned value can ...
Definition: sch_item.cpp:128
SCH_SCREEN * LastScreen()
Function LastScreen.
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
Class for a wire to bus entry.
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
This item represents a bus vector.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212
wxPoint GetPosition() const override
Function GetPosition.

References BUS, SCH_ITEM::Connection(), ERCE_BUS_ENTRY_CONFLICT, CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_BUS_ENTRY_BASE::GetPosition(), EDA_BASE_FRAME::GetUserUnits(), SCH_SHEET_PATH::LastScreen(), SCH_BUS_WIRE_ENTRY::m_connected_bus_item, CONNECTION_SUBGRAPH::m_driver, m_frame, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, MARKER_BASE::MARKER_ERC, 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 1923 of file connection_graph.cpp.

1924 {
1925  auto sheet = aSubgraph->m_sheet;
1926  auto screen = sheet.LastScreen();
1927 
1928  SCH_ITEM* net_item = nullptr;
1929  SCH_ITEM* bus_item = nullptr;
1930  SCH_CONNECTION conn;
1931 
1932  for( auto item : aSubgraph->m_items )
1933  {
1934  switch( item->Type() )
1935  {
1936  case SCH_LINE_T:
1937  {
1938  if( item->GetLayer() == LAYER_BUS )
1939  bus_item = ( !bus_item ) ? item : bus_item;
1940  else
1941  net_item = ( !net_item ) ? item : net_item;
1942  break;
1943  }
1944 
1945  case SCH_GLOBAL_LABEL_T:
1946  case SCH_SHEET_PIN_T:
1947  case SCH_HIER_LABEL_T:
1948  {
1949  auto text = static_cast<SCH_TEXT*>( item )->GetShownText();
1950  conn.ConfigureFromLabel( text );
1951 
1952  if( conn.IsBus() )
1953  bus_item = ( !bus_item ) ? item : bus_item;
1954  else
1955  net_item = ( !net_item ) ? item : net_item;
1956  break;
1957  }
1958 
1959  default:
1960  break;
1961  }
1962  }
1963 
1964  if( net_item && bus_item )
1965  {
1966  auto marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
1967  marker->SetData( m_frame->GetUserUnits(), ERCE_BUS_TO_NET_CONFLICT,
1968  net_item->GetPosition(), net_item, bus_item );
1969  screen->Append( marker );
1970 
1971  return false;
1972  }
1973 
1974  return true;
1975 }
SCH_SHEET_PATH m_sheet
virtual wxPoint GetPosition() const =0
Function GetPosition.
std::vector< SCH_ITEM * > m_items
SCH_EDIT_FRAME * m_frame
void ConfigureFromLabel(wxString aLabel)
Configures the connection given a label.
SCH_SCREEN * LastScreen()
Function LastScreen.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
bool IsBus() const
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
EDA_UNITS GetUserUnits() const
Return the user units currently in use.

References SCH_CONNECTION::ConfigureFromLabel(), ERCE_BUS_TO_NET_CONFLICT, SCH_ITEM::GetPosition(), EDA_BASE_FRAME::GetUserUnits(), SCH_CONNECTION::IsBus(), SCH_SHEET_PATH::LastScreen(), LAYER_BUS, m_frame, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, MARKER_BASE::MARKER_ERC, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LINE_T, and SCH_SHEET_PIN_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 2241 of file connection_graph.cpp.

2242 {
2243  // Label connection rules:
2244  // Local labels are flagged if they don't connect to any pins and don't have a no-connect
2245  // Global labels are flagged if they appear only once, don't connect to any local labels,
2246  // and don't have a no-connect marker
2247 
2248  // So, if there is a no-connect, we will never generate a warning here
2249  if( aSubgraph->m_no_connect )
2250  return true;
2251 
2252  SCH_TEXT* text = nullptr;
2253  bool has_other_connections = false;
2254 
2255  for( auto item : aSubgraph->m_items )
2256  {
2257  switch( item->Type() )
2258  {
2259  case SCH_LABEL_T:
2260  case SCH_GLOBAL_LABEL_T:
2261  case SCH_HIER_LABEL_T:
2262  text = static_cast<SCH_TEXT*>( item );
2263  break;
2264 
2265  case SCH_PIN_T:
2266  case SCH_SHEET_PIN_T:
2267  has_other_connections = true;
2268  break;
2269 
2270  default:
2271  break;
2272  }
2273  }
2274 
2275  if( !text )
2276  return true;
2277 
2278  bool is_global = text->Type() == SCH_GLOBAL_LABEL_T;
2279 
2280  // Global label check can be disabled independently
2281  if( !g_ErcSettings->IsTestEnabled( ERCE_GLOBLABEL ) && is_global )
2282  return true;
2283 
2284  wxString name = text->GetShownText();
2285 
2286  if( is_global )
2287  {
2288  // This will be set to true if the global is connected to a pin above, but we
2289  // want to reset this to false so that globals get flagged if they only have a
2290  // single instance
2291  has_other_connections = false;
2292 
2293  if( m_net_name_to_subgraphs_map.count( name )
2294  && m_net_name_to_subgraphs_map.at( name ).size() > 1 )
2295  has_other_connections = true;
2296  }
2297  else if( text->Type() == SCH_HIER_LABEL_T )
2298  {
2299  // For a hier label, check if the parent pin is connected
2300  if( aSubgraph->m_hier_parent &&
2301  ( aSubgraph->m_hier_parent->m_strong_driver ||
2302  aSubgraph->m_hier_parent->m_drivers.size() > 1))
2303  {
2304  // For now, a simple check: if there is more than one driver, the parent is probably
2305  // connected elsewhere (because at least one driver will be the hier pin itself)
2306  has_other_connections = true;
2307  }
2308  }
2309  else
2310  {
2311  auto pair = std::make_pair( aSubgraph->m_sheet, name );
2312 
2313  if( m_local_label_cache.count( pair ) && m_local_label_cache.at( pair ).size() > 1 )
2314  has_other_connections = true;
2315  }
2316 
2317  if( !has_other_connections )
2318  {
2320  marker->SetData( m_frame->GetUserUnits(),
2322  text->GetPosition(), text );
2323  aSubgraph->m_sheet.LastScreen()->Append( marker );
2324 
2325  return false;
2326  }
2327 
2328  return true;
2329 }
SCH_SHEET_PATH m_sheet
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:68
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
ERC_SETTINGS * g_ErcSettings
This also wants to live in the eventual SCHEMATIC object.
std::vector< SCH_ITEM * > m_items
SCH_EDIT_FRAME * m_frame
bool m_strong_driver
True if the driver is "strong": a label or power object.
std::vector< SCH_ITEM * > m_drivers
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_text.h:317
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:175
CONNECTION_SUBGRAPH * m_hier_parent
virtual wxString GetShownText() const
Return the string actually shown after processing of the base text.
Definition: eda_text.h:129
void SetData(EDA_UNITS aUnits, int aErrorCode, const wxPoint &aMarkerPos, EDA_ITEM *aItem, const wxPoint &aPos, EDA_ITEM *bItem=nullptr, const wxPoint &bPos=wxPoint())
Function SetData fills in all the reportable data associated with a MARKER.
Definition: marker_base.cpp:91
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
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212

References SCH_SCREEN::Append(), ERCE_GLOBLABEL, ERCE_LABEL_NOT_CONNECTED, g_ErcSettings, SCH_TEXT::GetPosition(), EDA_TEXT::GetShownText(), EDA_BASE_FRAME::GetUserUnits(), ERC_SETTINGS::IsTestEnabled(), SCH_SHEET_PATH::LastScreen(), CONNECTION_SUBGRAPH::m_drivers, m_frame, CONNECTION_SUBGRAPH::m_hier_parent, CONNECTION_SUBGRAPH::m_items, m_local_label_cache, m_net_name_to_subgraphs_map, CONNECTION_SUBGRAPH::m_no_connect, CONNECTION_SUBGRAPH::m_sheet, CONNECTION_SUBGRAPH::m_strong_driver, MARKER_BASE::MARKER_ERC, name, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T, SCH_PIN_T, SCH_SHEET_PIN_T, MARKER_BASE::SetData(), 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 2125 of file connection_graph.cpp.

2126 {
2127  wxString msg;
2128  auto sheet = aSubgraph->m_sheet;
2129  auto screen = sheet.LastScreen();
2130 
2131  if( aSubgraph->m_no_connect != nullptr )
2132  {
2133  bool has_invalid_items = false;
2134  bool has_other_items = false;
2135  SCH_PIN* pin = nullptr;
2136  std::vector<SCH_ITEM*> invalid_items;
2137 
2138  // Any subgraph that contains both a pin and a no-connect should not
2139  // contain any other driving items.
2140 
2141  for( auto item : aSubgraph->m_items )
2142  {
2143  switch( item->Type() )
2144  {
2145  case SCH_PIN_T:
2146  pin = static_cast<SCH_PIN*>( item );
2147  has_other_items = true;
2148  break;
2149 
2150  case SCH_LINE_T:
2151  case SCH_JUNCTION_T:
2152  case SCH_NO_CONNECT_T:
2153  break;
2154 
2155  default:
2156  has_invalid_items = true;
2157  has_other_items = true;
2158  invalid_items.push_back( item );
2159  }
2160  }
2161 
2162  if( pin && has_invalid_items )
2163  {
2164  auto marker = new SCH_MARKER( MARKER_BASE::MARKER_ERC );
2165  marker->SetData( ERCE_NOCONNECT_CONNECTED, pin->GetTransformedPosition(),
2166  pin->GetDescription( &aSubgraph->m_sheet ), pin->m_Uuid );
2167  screen->Append( marker );
2168 
2169  return false;
2170  }
2171 
2172  if( !has_other_items )
2173  {
2176  aSubgraph->m_no_connect->GetPosition(), aSubgraph->m_no_connect );
2177  screen->Append( marker );
2178 
2179  return false;
2180  }
2181  }
2182  else
2183  {
2184  bool has_other_connections = false;
2185  SCH_PIN* pin = nullptr;
2186 
2187  // Any subgraph that lacks a no-connect and contains a pin should also
2188  // contain at least one other connectable item.
2189 
2190  for( auto item : aSubgraph->m_items )
2191  {
2192  switch( item->Type() )
2193  {
2194  case SCH_PIN_T:
2195  if( !pin )
2196  pin = static_cast<SCH_PIN*>( item );
2197  else
2198  has_other_connections = true;
2199 
2200  break;
2201 
2202  default:
2203  if( item->IsConnectable() )
2204  has_other_connections = true;
2205 
2206  break;
2207  }
2208  }
2209 
2210  // Check if invisible power pins connect to anything else.
2211  // Note this won't catch a component with multiple invisible power pins but these don't
2212  // connect to any other net; maybe that should be added as a further optional ERC check?
2213 
2214  if( pin && !has_other_connections && pin->IsPowerConnection() && !pin->IsVisible() )
2215  {
2216  wxString name = pin->Connection( sheet )->Name();
2217  wxString local_name = pin->Connection( sheet )->Name( true );
2218 
2219  if( m_global_label_cache.count( name ) ||
2220  ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
2221  {
2222  has_other_connections = true;
2223  }
2224  }
2225 
2226  if( pin && !has_other_connections && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC )
2227  {
2230  pin->GetDescription( &aSubgraph->m_sheet ), pin->m_Uuid );
2231  screen->Append( marker );
2232 
2233  return false;
2234  }
2235  }
2236 
2237  return true;
2238 }
SCH_SHEET_PATH m_sheet
bool IsVisible() const
Definition: sch_pin.h:100
wxString GetDescription(const SCH_SHEET_PATH *aSheet)
Definition: sch_pin.cpp:84
virtual wxPoint GetPosition() const =0
Function GetPosition.
wxString Name(bool aIgnoreSheet=false) const
std::vector< SCH_ITEM * > m_items
SCH_EDIT_FRAME * m_frame
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieves the connection associated with this object in the given sheet Note: the returned value can ...
Definition: sch_item.cpp:128
const KIID m_Uuid
Definition: base_struct.h:169
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
ELECTRICAL_PINTYPE GetType() const
Definition: sch_pin.h:106
bool IsPowerConnection() const
Definition: sch_pin.h:108
void SetData(EDA_UNITS aUnits, int aErrorCode, const wxPoint &aMarkerPos, EDA_ITEM *aItem, const wxPoint &aPos, EDA_ITEM *bItem=nullptr, const wxPoint &bPos=wxPoint())
Function SetData fills in all the reportable data associated with a MARKER.
Definition: marker_base.cpp:91
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)
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
wxPoint GetTransformedPosition() const
Returns the pin's position in global coordinates.
Definition: sch_pin.cpp:134

References SCH_ITEM::Connection(), ERCE_NOCONNECT_CONNECTED, ERCE_NOCONNECT_NOT_CONNECTED, ERCE_PIN_NOT_CONNECTED, SCH_PIN::GetDescription(), SCH_ITEM::GetPosition(), SCH_PIN::GetTransformedPosition(), SCH_PIN::GetType(), EDA_BASE_FRAME::GetUserUnits(), SCH_PIN::IsPowerConnection(), SCH_PIN::IsVisible(), SCH_SHEET_PATH::LastScreen(), m_frame, m_global_label_cache, CONNECTION_SUBGRAPH::m_items, m_local_label_cache, CONNECTION_SUBGRAPH::m_no_connect, CONNECTION_SUBGRAPH::m_sheet, EDA_ITEM::m_Uuid, MARKER_BASE::MARKER_ERC, name, SCH_CONNECTION::Name(), PT_NC, SCH_JUNCTION_T, SCH_LINE_T, SCH_NO_CONNECT_T, SCH_PIN_T, and MARKER_BASE::SetData().

Referenced by RunERC().

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

1817 {
1818  if( m_bus_alias_cache.count( aName ) )
1819  return m_bus_alias_cache.at( aName );
1820 
1821  return nullptr;
1822 }
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 1825 of file connection_graph.cpp.

1826 {
1827  std::vector<const CONNECTION_SUBGRAPH*> ret;
1828 
1829  for( auto&& subgraph : m_subgraphs )
1830  {
1831  // Graph is supposed to be up-to-date before calling this
1832  wxASSERT( !subgraph->m_dirty );
1833 
1834  if( !subgraph->m_driver )
1835  continue;
1836 
1837  auto sheet = subgraph->m_sheet;
1838  auto connection = subgraph->m_driver->Connection( sheet );
1839 
1840  if( !connection->IsBus() )
1841  continue;
1842 
1843  auto labels = subgraph->GetBusLabels();
1844 
1845  if( labels.size() > 1 )
1846  {
1847  bool different = false;
1848  wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetText();
1849 
1850  for( unsigned i = 1; i < labels.size(); ++i )
1851  {
1852  if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetText() != first )
1853  {
1854  different = true;
1855  break;
1856  }
1857  }
1858 
1859  if( !different )
1860  continue;
1861 
1862  wxLogTrace( "CONN", "SG %ld (%s) has multiple bus labels", subgraph->m_code,
1863  connection->Name() );
1864 
1865  ret.push_back( subgraph );
1866  }
1867  }
1868 
1869  return ret;
1870 }
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
wxString GetText(GRAPHIC_PINSHAPE aShape)
Definition: pin_shape.cpp:58

References GetText(), and m_subgraphs.

Referenced by DIALOG_MIGRATE_BUSES::loadGraphData(), and SCH_EDIT_FRAME::OpenProjectFiles().

◆ getDefaultConnection()

std::shared_ptr< SCH_CONNECTION > CONNECTION_GRAPH::getDefaultConnection ( SCH_ITEM aItem,
SCH_SHEET_PATH  aSheet 
)
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
Returns
a connection generated from the item, or nullptr if item is not valid

Definition at line 1710 of file connection_graph.cpp.

1712 {
1713  auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
1714 
1715  switch( aItem->Type() )
1716  {
1717  case SCH_PIN_T:
1718  {
1719  auto pin = static_cast<SCH_PIN*>( aItem );
1720 
1721  if( pin->IsPowerConnection() )
1722  {
1723  c = std::make_shared<SCH_CONNECTION>( aItem, aSheet );
1724  c->ConfigureFromLabel( pin->GetName() );
1725  }
1726  break;
1727  }
1728 
1729  case SCH_GLOBAL_LABEL_T:
1730  case SCH_HIER_LABEL_T:
1731  case SCH_LABEL_T:
1732  {
1733  auto text = static_cast<SCH_TEXT*>( aItem );
1734 
1735  c = std::make_shared<SCH_CONNECTION>( aItem, aSheet );
1736  c->ConfigureFromLabel( text->GetText() );
1737  break;
1738  }
1739 
1740  default:
1741  break;
1742  }
1743 
1744  return c;
1745 }
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212

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

Referenced by buildConnectionGraph(), and propagateToNeighbors().

◆ GetNetMap()

const NET_MAP& CONNECTION_GRAPH::GetNetMap ( ) const
inline

Definition at line 261 of file connection_graph.h.

261 { return m_net_code_to_subgraphs_map; }
NET_MAP m_net_code_to_subgraphs_map

References m_net_code_to_subgraphs_map.

Referenced by NETLIST_EXPORTER_GENERIC::makeListOfNets().

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

1750 {
1751  wxASSERT( aBusConnection->IsBus() );
1752 
1753  SCH_CONNECTION* match = nullptr;
1754 
1755  if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
1756  {
1757  // Vector bus: compare against index, because we allow the name
1758  // to be different
1759 
1760  for( const auto& bus_member : aBusConnection->Members() )
1761  {
1762  if( bus_member->VectorIndex() == aSearch->VectorIndex() )
1763  {
1764  match = bus_member.get();
1765  break;
1766  }
1767  }
1768  }
1769  else
1770  {
1771  // Group bus
1772  for( const auto& c : aBusConnection->Members() )
1773  {
1774  // Vector inside group: compare names, because for bus groups
1775  // we expect the naming to be consistent across all usages
1776  // TODO(JE) explain this in the docs
1777  if( c->Type() == CONNECTION_TYPE::BUS )
1778  {
1779  for( const auto& bus_member : c->Members() )
1780  {
1781  if( bus_member->LocalName() == aSearch->LocalName() )
1782  {
1783  match = bus_member.get();
1784  break;
1785  }
1786  }
1787  }
1788  else if( c->LocalName() == aSearch->LocalName() )
1789  {
1790  match = c.get();
1791  break;
1792  }
1793  }
1794  }
1795 
1796  return match;
1797 }
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 1440 of file connection_graph.cpp.

1441 {
1442  SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
1443  std::vector<CONNECTION_SUBGRAPH*> search_list;
1444  std::unordered_set<CONNECTION_SUBGRAPH*> visited;
1445  std::vector<SCH_CONNECTION*> stale_bus_members;
1446 
1447  auto visit = [&] ( CONNECTION_SUBGRAPH* aParent ) {
1448  for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
1449  {
1450  SCH_SHEET_PATH path = aParent->m_sheet;
1451  path.push_back( pin->GetParent() );
1452 
1453  if( !m_sheet_to_subgraphs_map.count( path ) )
1454  continue;
1455 
1456  for( auto candidate : m_sheet_to_subgraphs_map.at( path ) )
1457  {
1458  if( !candidate->m_strong_driver ||
1459  candidate->m_hier_ports.empty() ||
1460  visited.count( candidate ) )
1461  continue;
1462 
1463  for( SCH_HIERLABEL* label : candidate->m_hier_ports )
1464  {
1465  if( label->GetShownText() == pin->GetShownText() )
1466  {
1467  wxLogTrace( "CONN", "%lu: found child %lu (%s)", aParent->m_code,
1468  candidate->m_code, candidate->m_driver_connection->Name() );
1469 
1470  candidate->m_hier_parent = aParent;
1471 
1472  search_list.push_back( candidate );
1473  break;
1474  }
1475  }
1476  }
1477  }
1478 
1479  for( SCH_HIERLABEL* label : aParent->m_hier_ports )
1480  {
1481  SCH_SHEET_PATH path = aParent->m_sheet;
1482  path.pop_back();
1483 
1484  if( !m_sheet_to_subgraphs_map.count( path ) )
1485  continue;
1486 
1487  for( auto candidate : m_sheet_to_subgraphs_map.at( path ) )
1488  {
1489  if( candidate->m_hier_pins.empty() ||
1490  visited.count( candidate ) ||
1491  ( candidate->m_driver_connection->Type() !=
1492  aParent->m_driver_connection->Type() ) )
1493  continue;
1494 
1495  for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
1496  {
1497  SCH_SHEET_PATH pin_path = path;
1498  pin_path.push_back( pin->GetParent() );
1499 
1500  if( pin_path != aParent->m_sheet )
1501  continue;
1502 
1503  if( label->GetShownText() == pin->GetShownText() )
1504  {
1505  wxLogTrace( "CONN", "%lu: found additional parent %lu (%s)",
1506  aParent->m_code, candidate->m_code,
1507  candidate->m_driver_connection->Name() );
1508 
1509  search_list.push_back( candidate );
1510  break;
1511  }
1512  }
1513  }
1514  }
1515  };
1516 
1517  auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
1518  for( const auto& kv : aParentGraph->m_bus_neighbors )
1519  {
1520  for( CONNECTION_SUBGRAPH* neighbor : kv.second )
1521  {
1522  // May have been absorbed but won't have been deleted
1523  while( neighbor->m_absorbed )
1524  neighbor = neighbor->m_absorbed_by;
1525 
1526  SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
1527 
1528  // Now member may be out of date, since we just cloned the
1529  // connection from higher up in the hierarchy. We need to
1530  // figure out what the actual new connection is.
1531  SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
1532 
1533  if( !member )
1534  {
1535  // Try harder: we might match on a secondary driver
1536  for( CONNECTION_SUBGRAPH* sg : kv.second )
1537  {
1538  if( sg->m_multiple_drivers )
1539  {
1540  SCH_SHEET_PATH sheet = sg->m_sheet;
1541 
1542  for( SCH_ITEM* driver : sg->m_drivers )
1543  {
1544  auto c = getDefaultConnection( driver, sheet );
1545  member = matchBusMember( parent, c.get() );
1546 
1547  if( member )
1548  break;
1549  }
1550  }
1551 
1552  if( member )
1553  break;
1554  }
1555  }
1556 
1557  // This is bad, probably an ERC error
1558  if( !member )
1559  {
1560  wxLogTrace( "CONN", "Could not match bus member %s in %s",
1561  kv.first->Name(), parent->Name() );
1562  continue;
1563  }
1564 
1565  auto neighbor_conn = neighbor->m_driver_connection;
1566  auto neighbor_name = neighbor_conn->Name();
1567 
1568  // Matching name: no update needed
1569  if( neighbor_name == member->Name() )
1570  continue;
1571 
1572  // Safety check against infinite recursion
1573  wxASSERT( neighbor_conn->IsNet() );
1574 
1575  wxLogTrace( "CONN", "%lu (%s) connected to bus member %s (local %s)",
1576  neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
1577 
1578  // Take whichever name is higher priority
1581  {
1582  member->Clone( *neighbor_conn );
1583  stale_bus_members.push_back( member );
1584  }
1585  else
1586  {
1587  neighbor_conn->Clone( *member );
1588  neighbor->UpdateItemConnections();
1589 
1590  recacheSubgraphName( neighbor, neighbor_name );
1591 
1592  // Recurse onto this neighbor in case it needs to re-propagate
1593  neighbor->m_dirty = true;
1594  propagateToNeighbors( neighbor );
1595  }
1596  }
1597  }
1598  };
1599 
1600  // If we are a bus, we must propagate to local neighbors and then the hierarchy
1601  if( conn->IsBus() )
1602  propagate_bus_neighbors( aSubgraph );
1603 
1604  // If we don't have any hier pins (i.e. no children), nothing to do
1605  if( aSubgraph->m_hier_pins.empty() )
1606  {
1607  // If we also don't have any parents, we'll never be visited again
1608  if( aSubgraph->m_hier_ports.empty() )
1609  aSubgraph->m_dirty = false;
1610 
1611  return;
1612  }
1613 
1614  // If we do have hier ports, skip this subgraph as it will be visited by a parent
1615  // TODO(JE) this will leave the subgraph dirty if there is no matching parent subgraph,
1616  // which should be flagged as an ERC error
1617  if( !aSubgraph->m_hier_ports.empty() )
1618  return;
1619 
1620  visited.insert( aSubgraph );
1621 
1622  wxLogTrace( "CONN", "Propagating %lu (%s) to subsheets",
1623  aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
1624 
1625  visit( aSubgraph );
1626 
1627  for( CONNECTION_SUBGRAPH* child : search_list )
1628  {
1629  visited.insert( child );
1630 
1631  visit( child );
1632 
1633  child->m_dirty = false;
1634  }
1635 
1636  // Now, find the best driver for this chain of subgraphs
1637  CONNECTION_SUBGRAPH* driver = aSubgraph;
1640 
1641  // Check if a subsheet has a higher-priority connection to the same net
1643  {
1644  for( CONNECTION_SUBGRAPH* subgraph : visited )
1645  {
1647  CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
1648 
1649  // Upgrade driver to be this subgraph if this subgraph has a power pin or global
1650  // Also upgrade if we found something with a shorter sheet path (higher in hierarchy)
1651  // but with an equivalent priority
1652 
1653  if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
1654  ( priority >= highest && subgraph->m_sheet.size() < aSubgraph->m_sheet.size() ) )
1655  driver = subgraph;
1656  }
1657  }
1658 
1659  if( driver != aSubgraph )
1660  {
1661  wxLogTrace( "CONN", "%lu (%s) overridden by new driver %lu (%s)",
1662  aSubgraph->m_code, aSubgraph->m_driver_connection->Name(),
1663  driver->m_code, driver->m_driver_connection->Name() );
1664  }
1665 
1666  conn = driver->m_driver_connection;
1667 
1668  for( CONNECTION_SUBGRAPH* subgraph : visited )
1669  {
1670  wxString old_name = subgraph->m_driver_connection->Name();
1671 
1672  subgraph->m_driver_connection->Clone( *conn );
1673  subgraph->UpdateItemConnections();
1674 
1675  if( old_name != conn->Name() )
1676  recacheSubgraphName( subgraph, old_name );
1677 
1678  if( conn->IsBus() )
1679  propagate_bus_neighbors( subgraph );
1680  }
1681 
1682  // Somewhere along the way, a bus member may have been upgraded to a global or power label.
1683  // Because this can happen anywhere, we need a second pass to update all instances of that bus
1684  // member to have the correct connection info
1685  if( conn->IsBus() && !stale_bus_members.empty() )
1686  {
1687  for( auto stale_member : stale_bus_members )
1688  {
1689  for( CONNECTION_SUBGRAPH* subgraph : visited )
1690  {
1691  SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
1692  stale_member );
1693  wxASSERT( member );
1694 
1695  wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", subgraph->m_code,
1696  subgraph->m_driver_connection->Name(), member->LocalName(),
1697  stale_member->Name() );
1698 
1699  member->Clone( *stale_member );
1700 
1701  propagate_bus_neighbors( subgraph );
1702  }
1703  }
1704  }
1705 
1706  aSubgraph->m_dirty = false;
1707 }
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:167
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
std::vector< SCH_SHEET_PIN * > m_hier_pins
wxString LocalName() const
#define kv
A subgraph is a set of items that are electrically connected on a single sheet.
void pop_back()
Forwarded method from std::vector.
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
void recacheSubgraphName(CONNECTION_SUBGRAPH *aSubgraph, const wxString &aOldName)
wxString Name(bool aIgnoreSheet=false) const
size_t size() const
Forwarded method from std::vector.
std::vector< SCH_ITEM * > m_drivers
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
SCH_SHEET_PATH.
void Clone(SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
std::vector< SCH_HIERLABEL * > m_hier_ports
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
std::shared_ptr< SCH_CONNECTION > getDefaultConnection(SCH_ITEM *aItem, SCH_SHEET_PATH aSheet)
Builds a new default connection for the given item based on its properties.
virtual wxString GetShownText() const
Return the string actually shown after processing of the base text.
Definition: eda_text.h:129
bool IsBus() const
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
static SCH_CONNECTION * matchBusMember(SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
Search for a matching bus member inside a bus connection.
void UpdateItemConnections()
Updates all items to match the driver connection.

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

Referenced by buildConnectionGraph().

◆ recacheSubgraphName()

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

Definition at line 1800 of file connection_graph.cpp.

1802 {
1803  if( m_net_name_to_subgraphs_map.count( aOldName ) )
1804  {
1805  auto& vec = m_net_name_to_subgraphs_map.at( aOldName );
1806  vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
1807  }
1808 
1809  wxLogTrace( "CONN", "recacheSubgraphName: %s => %s", aOldName,
1810  aSubgraph->m_driver_connection->Name() );
1811 
1812  m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
1813 }
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
wxString Name(bool aIgnoreSheet=false) const

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

Referenced by propagateToNeighbors().

◆ Recalculate()

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

Updates the connection graph for the given list of sheets.

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

Definition at line 364 of file connection_graph.cpp.

365 {
366  PROF_COUNTER recalc_time;
367  PROF_COUNTER update_items;
368 
369  if( aUnconditional )
370  Reset();
371 
372  for( const SCH_SHEET_PATH& sheet : aSheetList )
373  {
374  std::vector<SCH_ITEM*> items;
375 
376  for( auto item : sheet.LastScreen()->Items() )
377  {
378  if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
379  items.push_back( item );
380  }
381 
382  updateItemConnectivity( sheet, items );
383 
384  // UpdateDanglingState() also adds connected items for SCH_TEXT
385  sheet.LastScreen()->TestDanglingEnds( &sheet );
386  }
387 
388  update_items.Stop();
389  wxLogTrace( "CONN_PROFILE", "UpdateItemConnectivity() %0.4f ms", update_items.msecs() );
390 
391  PROF_COUNTER build_graph;
392 
394 
395  build_graph.Stop();
396  wxLogTrace( "CONN_PROFILE", "BuildConnectionGraph() %0.4f ms", build_graph.msecs() );
397 
398  recalc_time.Stop();
399  wxLogTrace( "CONN_PROFILE", "Recalculate time %0.4f ms", recalc_time.msecs() );
400 
401 #ifndef DEBUG
402  // Pressure relief valve for release builds
403  const double max_recalc_time_msecs = 250.;
404 
406  recalc_time.msecs() > max_recalc_time_msecs )
407  {
408  m_allowRealTime = false;
409  }
410 #endif
411 }
void Stop()
save the time when this function was called, and set the counter stane to stop
Definition: profile.h:82
void buildConnectionGraph()
Generates the connection graph (after all item connectivity has been updated)
double msecs(bool aSinceLast=false)
Definition: profile.h:143
The class PROF_COUNTER is a small class to help profiling.
Definition: profile.h:44
SCH_SHEET_PATH.
static bool m_allowRealTime
bool m_realTimeConnectivity
Do real-time connectivity.
void updateItemConnectivity(SCH_SHEET_PATH aSheet, const std::vector< SCH_ITEM * > &aItemList)
Updates the graphical connectivity between items (i.e.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.

References buildConnectionGraph(), ADVANCED_CFG::GetCfg(), m_allowRealTime, ADVANCED_CFG::m_realTimeConnectivity, PROF_COUNTER::msecs(), Reset(), PROF_COUNTER::Stop(), and updateItemConnectivity().

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

◆ Reset()

void CONNECTION_GRAPH::Reset ( )

Definition at line 341 of file connection_graph.cpp.

342 {
343  for( auto& subgraph : m_subgraphs )
344  delete subgraph;
345 
346  m_items.clear();
347  m_subgraphs.clear();
348  m_driver_subgraphs.clear();
349  m_sheet_to_subgraphs_map.clear();
350  m_invisible_power_pins.clear();
351  m_bus_alias_cache.clear();
352  m_net_name_to_code_map.clear();
353  m_bus_name_to_code_map.clear();
356  m_local_label_cache.clear();
357  m_global_label_cache.clear();
358  m_last_net_code = 1;
359  m_last_bus_code = 1;
361 }
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::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::unordered_set< SCH_ITEM * > m_items
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache

References m_bus_alias_cache, m_bus_name_to_code_map, m_driver_subgraphs, m_global_label_cache, m_invisible_power_pins, 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::CreateScreens(), SCH_EDIT_FRAME::OpenProjectFiles(), Recalculate(), SCH_EDIT_FRAME::rescueProject(), 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 1873 of file connection_graph.cpp.

1874 {
1875  int error_count = 0;
1876 
1877  for( auto&& subgraph : m_subgraphs )
1878  {
1879  // Graph is supposed to be up-to-date before calling RunERC()
1880  wxASSERT( !subgraph->m_dirty );
1881 
1893  if( g_ErcSettings->IsTestEnabled( ERCE_DRIVER_CONFLICT ) && !subgraph->ResolveDrivers() )
1894  error_count++;
1895 
1897  && !ercCheckBusToNetConflicts( subgraph ) )
1898  error_count++;
1899 
1901  && !ercCheckBusToBusEntryConflicts( subgraph ) )
1902  error_count++;
1903 
1905  && !ercCheckBusToBusConflicts( subgraph ) )
1906  error_count++;
1907 
1908  // The following checks are always performed since they don't currently
1909  // have an option exposed to the user
1910 
1911  if( !ercCheckNoConnects( subgraph ) )
1912  error_count++;
1913 
1915  || g_ErcSettings->IsTestEnabled( ERCE_GLOBLABEL ) ) && !ercCheckLabels( subgraph ) )
1916  error_count++;
1917  }
1918 
1919  return error_count;
1920 }
bool ercCheckBusToBusEntryConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting bus entry to bus connections.
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:68
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
ERC_SETTINGS * g_ErcSettings
This also wants to live in the eventual SCHEMATIC object.
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.
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(), ercCheckLabels(), ercCheckNoConnects(), ERCE_BUS_ENTRY_CONFLICT, ERCE_BUS_TO_BUS_CONFLICT, ERCE_BUS_TO_NET_CONFLICT, ERCE_DRIVER_CONFLICT, ERCE_GLOBLABEL, ERCE_LABEL_NOT_CONNECTED, g_ErcSettings, ERC_SETTINGS::IsTestEnabled(), and m_subgraphs.

Referenced by DIALOG_ERC::TestErc().

◆ updateItemConnectivity()

void CONNECTION_GRAPH::updateItemConnectivity ( 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 414 of file connection_graph.cpp.

416 {
417  std::unordered_map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
418 
419  for( SCH_ITEM* item : aItemList )
420  {
421  std::vector< wxPoint > points;
422  item->GetConnectionPoints( points );
423  item->ConnectedItems( aSheet ).clear();
424 
425  if( item->Type() == SCH_SHEET_T )
426  {
427  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
428  {
429  if( !pin->Connection( aSheet ) )
430  pin->InitializeConnection( aSheet );
431 
432  pin->ConnectedItems( aSheet ).clear();
433  pin->Connection( aSheet )->Reset();
434 
435  connection_map[ pin->GetTextPos() ].push_back( pin );
436  m_items.insert( pin );
437  }
438  }
439  else if( item->Type() == SCH_COMPONENT_T )
440  {
441  SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
442  TRANSFORM t = component->GetTransform();
443 
444  // TODO(JE) right now this relies on GetSchPins() returning good SCH_PIN pointers
445  // that contain good LIB_PIN pointers. Since these get invalidated whenever the
446  // library component is refreshed, the current solution as of ed025972 is to just
447  // rebuild the SCH_PIN list when the component is refreshed, and then re-run the
448  // connectivity calculations. This is slow and should be improved before release.
449  // See https://gitlab.com/kicad/code/kicad/issues/3784
450 
451  for( SCH_PIN* pin : component->GetSchPins( &aSheet ) )
452  {
453  pin->InitializeConnection( aSheet );
454 
455  wxPoint pos = t.TransformCoordinate( pin->GetPosition() ) +
456  component->GetPosition();
457 
458  // because calling the first time is not thread-safe
459  pin->GetDefaultNetName( aSheet );
460  pin->ConnectedItems( aSheet ).clear();
461 
462  // Invisible power pins need to be post-processed later
463 
464  if( pin->IsPowerConnection() && !pin->IsVisible() )
465  m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
466 
467  connection_map[ pos ].push_back( pin );
468  m_items.insert( pin );
469  }
470  }
471  else
472  {
473  m_items.insert( item );
474  auto conn = item->InitializeConnection( aSheet );
475 
476  // Set bus/net property here so that the propagation code uses it
477  switch( item->Type() )
478  {
479  case SCH_LINE_T:
480  conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
482  break;
483 
484  case SCH_BUS_BUS_ENTRY_T:
485  conn->SetType( CONNECTION_TYPE::BUS );
486  // clean previous (old) links:
487  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
488  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
489  break;
490 
491  case SCH_PIN_T:
492  conn->SetType( CONNECTION_TYPE::NET );
493  break;
494 
496  conn->SetType( CONNECTION_TYPE::NET );
497  // clean previous (old) link:
498  static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
499  break;
500 
501  default:
502  break;
503  }
504 
505  for( const wxPoint& point : points )
506  connection_map[ point ].push_back( item );
507  }
508 
509  item->SetConnectivityDirty( false );
510  }
511 
512  for( const auto& it : connection_map )
513  {
514  auto connection_vec = it.second;
515 
516  for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
517  {
518  SCH_ITEM* connected_item = *primary_it;
519 
520  // Bus entries are special: they can have connection points in the
521  // middle of a wire segment, because the junction algo doesn't split
522  // the segment in two where you place a bus entry. This means that
523  // bus entries that don't land on the end of a line segment need to
524  // have "virtual" connection points to the segments they graphically
525  // touch.
526  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
527  {
528  // If this location only has the connection point of the bus
529  // entry itself, this means that either the bus entry is not
530  // connected to anything graphically, or that it is connected to
531  // a segment at some point other than at one of the endpoints.
532  if( connection_vec.size() == 1 )
533  {
534  SCH_SCREEN* screen = aSheet.LastScreen();
535  SCH_LINE* bus = screen->GetBus( it.first );
536 
537  if( bus )
538  {
539  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
540  bus_entry->m_connected_bus_item = bus;
541  }
542  }
543  }
544 
545  // Bus-to-bus entries are treated just like bus wires
546  if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
547  {
548  if( connection_vec.size() < 2 )
549  {
550  SCH_SCREEN* screen = aSheet.LastScreen();
551  SCH_LINE* bus = screen->GetBus( it.first );
552 
553  if( bus )
554  {
555  auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
556 
557  if( it.first == bus_entry->GetPosition() )
558  bus_entry->m_connected_bus_items[0] = bus;
559  else
560  bus_entry->m_connected_bus_items[1] = bus;
561 
562  bus_entry->ConnectedItems( aSheet ).insert( bus );
563  bus->ConnectedItems( aSheet ).insert( bus_entry );
564  }
565  }
566  }
567 
568  for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
569  {
570  auto test_item = *test_it;
571 
572  if( connected_item != test_item &&
573  connected_item->ConnectionPropagatesTo( test_item ) &&
574  test_item->ConnectionPropagatesTo( connected_item ) )
575  {
576  connected_item->ConnectedItems( aSheet ).insert( test_item );
577  test_item->ConnectedItems( aSheet ).insert( connected_item );
578  }
579 
580  // Set up the link between the bus entry net and the bus
581  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
582  {
583  if( test_item->Connection( aSheet )->IsBus() )
584  {
585  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
586  bus_entry->m_connected_bus_item = test_item;
587  }
588  }
589  }
590 
591  // If we got this far and did not find a connected bus item for a bus entry,
592  // we should do a manual scan in case there is a bus item on this connection
593  // point but we didn't pick it up earlier because there is *also* a net item here.
594  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
595  {
596  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
597 
598  if( !bus_entry->m_connected_bus_item )
599  {
600  auto screen = aSheet.LastScreen();
601  auto bus = screen->GetBus( it.first );
602 
603  if( bus )
604  bus_entry->m_connected_bus_item = bus;
605  }
606  }
607  }
608  }
609 }
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:138
virtual bool ConnectionPropagatesTo(const EDA_ITEM *aItem) const
Returns true if this item should propagate connection info to aItem.
Definition: sch_item.h:393
wxPoint TransformCoordinate(const wxPoint &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition: transform.cpp:42
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:45
TRANSFORM & GetTransform() const
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
This item represents a net.
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
SCH_SCREEN * LastScreen()
Function LastScreen.
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:38
SCH_COMPONENT describes a real schematic component.
Definition: sch_component.h:99
wxPoint GetPosition() const override
Function GetPosition.
std::unordered_set< SCH_ITEM * > m_items
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
SCH_PIN_PTRS GetSchPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieves a list of the SCH_PINs for the given sheet path.
This item represents a bus vector.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212
SCH_LINE * GetBus(const wxPoint &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T)
Definition: sch_screen.h:419

References BUS, SCH_ITEM::ConnectedItems(), SCH_ITEM::ConnectionPropagatesTo(), SCH_SCREEN::GetBus(), SCH_COMPONENT::GetPosition(), SCH_COMPONENT::GetSchPins(), SCH_COMPONENT::GetTransform(), SCH_SHEET_PATH::LastScreen(), LAYER_BUS, m_invisible_power_pins, m_items, NET, SCH_BUS_BUS_ENTRY_T, SCH_BUS_WIRE_ENTRY_T, SCH_COMPONENT_T, SCH_LINE_T, SCH_PIN_T, SCH_SHEET_T, TRANSFORM::TransformCoordinate(), 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 282 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 286 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 274 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_frame

◆ m_global_label_cache

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

Definition at line 288 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 280 of file connection_graph.h.

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

◆ m_item_mutex

std::mutex CONNECTION_GRAPH::m_item_mutex
private

Definition at line 304 of file connection_graph.h.

◆ m_items

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

Definition at line 268 of file connection_graph.h.

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

◆ m_last_bus_code

int CONNECTION_GRAPH::m_last_bus_code
private

Definition at line 300 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 298 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 302 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 296 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 284 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_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 278 of file connection_graph.h.

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

◆ m_subgraphs

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

Definition at line 271 of file connection_graph.h.

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


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