/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 CERN * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. * @author Jon Evans * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef _CONNECTION_GRAPH_H #define _CONNECTION_GRAPH_H #include #include #include #include #include #ifdef DEBUG // Uncomment this line to enable connectivity debugging features // #define CONNECTIVITY_DEBUG #endif class CONNECTION_GRAPH; class SCHEMATIC; class SCH_EDIT_FRAME; class SCH_HIERLABEL; class SCH_PIN; class SCH_SHEET_PIN; /** * A subgraph is a set of items that are electrically connected on a single sheet. * * For example, a label connected to a wire and so on. * A net is composed of one or more subgraphs. * * A set of items that appears to be physically connected may actually be more * than one subgraph, because some items don't connect electrically. * * For example, multiple bus wires can come together at a junction but have * different labels on each branch. Each label+wire branch is its own subgraph. * */ class CONNECTION_SUBGRAPH { public: enum class PRIORITY { INVALID = -1, NONE = 0, PIN, SHEET_PIN, HIER_LABEL, LOCAL_LABEL, POWER_PIN, GLOBAL }; explicit CONNECTION_SUBGRAPH( CONNECTION_GRAPH* aGraph ) : m_graph( aGraph ), m_dirty( false ), m_absorbed( false ), m_is_bus_member( false ), m_absorbed_by( nullptr ), m_code( -1 ), m_multiple_drivers( false ), m_strong_driver( false ), m_local_driver( false ), m_no_connect( nullptr ), m_bus_entry( nullptr ), m_driver( nullptr ), m_driver_connection( nullptr ), m_hier_parent( nullptr ) {} ~CONNECTION_SUBGRAPH() = default; /** * Determines which potential driver should drive the subgraph. * * If multiple possible drivers exist, picks one according to the priority. * If multiple "winners" exist, returns false and sets m_driver to nullptr. * * @param aCheckMultipleDrivers controls whether the second driver should be captured for ERC * @return true if m_driver was set, or false if a conflict occurred */ bool ResolveDrivers( bool aCheckMultipleDrivers = false ); /** * Returns the fully-qualified net name for this subgraph (if one exists) */ wxString GetNetName() const; /// Returns all the vector-based bus labels attached to this subgraph (if any) std::vector GetVectorBusLabels() const; /// Returns all the all bus labels attached to this subgraph (if any) std::vector GetAllBusLabels() const; /// Returns the candidate net name for a driver const wxString& GetNameForDriver( SCH_ITEM* aItem ) const; const wxString GetNetclassForDriver( SCH_ITEM* aItem ) const; /// Combines another subgraph on the same sheet into this one. void Absorb( CONNECTION_SUBGRAPH* aOther ); /// Adds a new item to the subgraph void AddItem( SCH_ITEM* aItem ); /// Updates all items to match the driver connection void UpdateItemConnections(); /// Provides a read-only reference to the items in the subgraph const std::set& GetItems() const { return m_items; } /// Finds all items in the subgraph as well as child subgraphs recursively void getAllConnectedItems( std::set>& aItems, std::set& aSubgraphs ); /** * Return the priority (higher is more important) of a candidate driver * * 0: Invalid driver * 1: Symbol pin * 2: Hierarchical sheet pin * 3: Hierarchical label * 4: Local label * 5: Power pin * 6: Global label * * @param aDriver is the item to inspect * @return a PRIORITY */ static PRIORITY GetDriverPriority( SCH_ITEM* aDriver ); PRIORITY GetDriverPriority() { if( m_driver ) return GetDriverPriority( m_driver ); else return PRIORITY::NONE; } void RemoveItem( SCH_ITEM* aItem ); private: wxString driverName( SCH_ITEM* aItem ) const; public: CONNECTION_GRAPH* m_graph; bool m_dirty; /// True if this subgraph has been absorbed into another. No pointers here are safe if so! bool m_absorbed; /** * True if the subgraph is not actually part of a net. These are created for bus members * to ensure that bus-to-bus connection happens but they don't have any valid data */ bool m_is_bus_member; /// If this subgraph is absorbed, points to the absorbing (and valid) subgraph CONNECTION_SUBGRAPH* m_absorbed_by; /// Set of subgraphs that have been absorbed by this subgraph std::set m_absorbed_subgraphs; long m_code; /** * True if this subgraph contains more than one driver that should be * shorted together in the netlist. For example, two labels or * two power ports. */ bool m_multiple_drivers; /// True if the driver is "strong": a label or power object bool m_strong_driver; /// True if the driver is a local (i.e. non-global) type bool m_local_driver; /// No-connect item in graph, if any SCH_ITEM* m_no_connect; /// Bus entry in graph, if any SCH_ITEM* m_bus_entry; std::set m_items; std::set m_drivers; SCH_ITEM* m_driver; SCH_SHEET_PATH m_sheet; /// Cache for driver connection SCH_CONNECTION* m_driver_connection; /** * If a subgraph is a bus, this map contains links between the bus members and any * local sheet neighbors with the same connection name. * * For example, if this subgraph is a bus D[7..0], and on the same sheet there is * a net with label D7, this map will contain an entry for the D7 bus member, and * the set will contain a pointer to the D7 net subgraph. */ std::unordered_map< std::shared_ptr, std::unordered_set > m_bus_neighbors; /** * If this is a net, this vector contains links to any same-sheet buses that contain it. * The string key is the name of the connection that forms the link (which isn't necessarily * the same as the name of the connection driving this subgraph) */ std::unordered_map< std::shared_ptr, std::unordered_set > m_bus_parents; // Cache for lookup of any hierarchical (sheet) pins on this subgraph (for referring down) std::set m_hier_pins; // Cache for lookup of any hierarchical ports on this subgraph (for referring up) std::set m_hier_ports; // If not null, this indicates the subgraph on a higher level sheet that is linked to this one CONNECTION_SUBGRAPH* m_hier_parent; // If not null, this indicates the subgraph(s) on a lower level sheet that are linked to this one std::unordered_set m_hier_children; /// A cache of escaped netnames from schematic items mutable std::unordered_map m_driver_name_cache; }; struct NET_NAME_CODE_CACHE_KEY { wxString Name; int Netcode; bool operator==(const NET_NAME_CODE_CACHE_KEY& other) const { return Name == other.Name && Netcode == other.Netcode; } }; namespace std { template <> struct hash { std::size_t operator()( const NET_NAME_CODE_CACHE_KEY& k ) const { const std::size_t prime = 19937; return hash()( k.Name ) ^ ( hash()( k.Netcode ) * prime ); } }; } /// Associates a NET_CODE_NAME with all the subgraphs in that net typedef std::unordered_map> NET_MAP; /** * Calculates the connectivity of a schematic and generates netlists */ class CONNECTION_GRAPH { public: CONNECTION_GRAPH( SCHEMATIC* aSchematic = nullptr ) : m_last_net_code( 1 ), m_last_bus_code( 1 ), m_last_subgraph_code( 1 ), m_schematic( aSchematic ) {} ~CONNECTION_GRAPH() { Reset(); } void Reset(); void SetSchematic( SCHEMATIC* aSchematic ) { m_schematic = aSchematic; } void SetLastCodes( const CONNECTION_GRAPH* aOther ) { m_last_net_code = aOther->m_last_net_code; m_last_bus_code = aOther->m_last_bus_code; m_last_subgraph_code = aOther->m_last_subgraph_code; } /** * Updates the connection graph for the given list of sheets. * * @param aSheetList is the list of possibly modified sheets * @param aUnconditional is true if an unconditional full recalculation should be done * @param aChangedItemHandler an optional handler to receive any changed items */ void Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional = false, std::function* aChangedItemHandler = nullptr ); /** * 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. */ std::shared_ptr GetBusAlias( const wxString& aName ); /** * Determines which subgraphs have more than one conflicting bus label. * * @see DIALOG_MIGRATE_BUSES * @return a list of subgraphs that need migration */ std::vector GetBusesNeedingMigration(); /** * Runs electrical rule checks on the connectivity graph. * * Precondition: graph is up-to-date * * @return the number of errors found */ int RunERC(); const NET_MAP& GetNetMap() const { return m_net_code_to_subgraphs_map; } /** * Returns the subgraph for a given net name on a given sheet * @param aNetName is the local net name to look for * @param aPath is a sheet path to look on * @return the subgraph matching the query, or nullptr if none is found */ CONNECTION_SUBGRAPH* FindSubgraphByName( const wxString& aNetName, const SCH_SHEET_PATH& aPath ); /** * Retrieves a subgraph for the given net name, if one exists. * Searches every sheet * @param aNetName is the full net name to search for * @return the subgraph matching the query, or nullptr if none is found */ CONNECTION_SUBGRAPH* FindFirstSubgraphByName( const wxString& aNetName ); CONNECTION_SUBGRAPH* GetSubgraphForItem( SCH_ITEM* aItem ); /** * Returns the fully-resolved netname for a given subgraph * @param aSubGraph Reference to the subgraph * @return Netname string usable with m_net_name_to_subgraphs_map */ wxString GetResolvedSubgraphName( const CONNECTION_SUBGRAPH* aSubGraph ) const; /** * For a set of items, this will remove the connected items and their * associated data including subgraphs and generated codes from the connection graph. * * @param aItems A vector of items whose presence should be removed from the graph * @return The full set of all items associated with the input items that were removed */ std::set> ExtractAffectedItems( const std::set &aItems ); /** * Combines the input graph contents into the current graph. After merging, the * original graph is invalid. * * @param aGraph Input graph reference to add to the current graph */ void Merge( CONNECTION_GRAPH& aGraph ); 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_SYMBOLs in this map -- we must store * a structure that links a specific pin on a symbol back to that symbol: a * SCH_PIN_CONNECTION. This wrapper class is a convenience for linking a pin * and symbol 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() * * @param aSheet is the path to the sheet of all items in the list * @param aItemList is a list of items to consider */ void updateItemConnectivity( const SCH_SHEET_PATH& aSheet, const std::vector& aItemList ); /** * 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. */ void buildConnectionGraph( std::function* aChangedItemHandler ); /** * Generates individual item subgraphs on a per-sheet basis */ void buildItemSubGraphs(); /** * Finds all subgraphs in the connection graph and calls ResolveDrivers() in * parallel */ void resolveAllDrivers(); /** * Maps the driver values for each subgraph */ void collectAllDriverValues(); /** * Iterate through the invisible power pins to collect the global labels * as drivers */ void generateInvisiblePinSubGraphs(); /** * Iterate through labels to create placeholders for bus elements */ void generateBusAliasMembers(); /** * Process all subgraphs to assign netcodes and merge subgraphs based on labels */ void processSubGraphs(); /** * Helper to assign a new net code to a connection * * @return the assigned code */ int assignNewNetCode( SCH_CONNECTION& aConnection ); /** * * @param aNetName string with the netname for coding * @return existing netcode (if it exists) or newly created one */ int getOrCreateNetCode( const wxString& aNetName ); /** * Ensures all members of the bus connection have a valid net code assigned * @param aConnection is a bus connection */ void assignNetCodesToBus( SCH_CONNECTION* aConnection ); /** * 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. * * @param aSubgraph is the subgraph being processed * @param aForce prevents this routine from skipping subgraphs */ void propagateToNeighbors( CONNECTION_SUBGRAPH* aSubgraph, bool aForce ); /** * Removes references to the given subgraphs from all structures in the * connection graph. * * @param aSubgraphs set of unique subgraphs to find/remove */ void removeSubgraphs( std::set& aSubgraphs ); /** * 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. * * @param aBusConnection is the bus connection to search * @param aSearch is the net connection to search for * @returns a member of aBusConnection that matches aSearch */ static SCH_CONNECTION* matchBusMember( SCH_CONNECTION* aBusConnection, SCH_CONNECTION* aSearch ); /** * Builds a new default connection for the given item based on its properties. * Handles strong drivers (power pins and labels) only * * @param aItem is an item that can generate a connection name * @param aSubgraph is used to determine the sheet to use and retrieve the cached name * @return a connection generated from the item, or nullptr if item is not valid */ std::shared_ptr getDefaultConnection( SCH_ITEM* aItem, CONNECTION_SUBGRAPH* aSubgraph ); void recacheSubgraphName( CONNECTION_SUBGRAPH* aSubgraph, const wxString& aOldName ); /** * If the subgraph has multiple drivers of equal priority that are graphically connected, * ResolveDrivers() will have stored the second driver for use by this function, which actually * creates the markers * @param aSubgraph is the subgraph to examine * @return true for no errors, false for errors */ bool ercCheckMultipleDrivers( const CONNECTION_SUBGRAPH* aSubgraph ); bool ercCheckNetclassConflicts( const std::vector& subgraphs ); /** * 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 * * @param aSubgraph is the subgraph to examine * @return true for no errors, false for errors */ bool ercCheckBusToNetConflicts( const CONNECTION_SUBGRAPH* aSubgraph ); /** * 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 * * @param aSubgraph is the subgraph to examine * @return true for no errors, false for errors */ bool ercCheckBusToBusConflicts( const CONNECTION_SUBGRAPH* aSubgraph ); /** * 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" * * @param aSubgraph is the subgraph to examine * @return true for no errors, false for errors */ bool ercCheckBusToBusEntryConflicts( const CONNECTION_SUBGRAPH* aSubgraph ); /** * 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 * * @param aSubgraph is the subgraph to examine * @return true for no errors, false for errors */ bool ercCheckNoConnects( const CONNECTION_SUBGRAPH* aSubgraph ); /** * Checks one subgraph for floating wires * * Will throw an error for any subgraph that consists of just wires with no driver * * @param aSubgraph is the subgraph to examine * @return true for no errors, false for errors */ bool ercCheckFloatingWires( const CONNECTION_SUBGRAPH* aSubgraph ); /** * Checks one subgraph for proper connection of labels * * Labels should be connected to something * * @param aSubgraph is the subgraph to examine * @param aCheckGlobalLabels is true if global labels should be checked for loneliness * @return true for no errors, false for errors */ bool ercCheckLabels( const CONNECTION_SUBGRAPH* aSubgraph ); /** * Checks that a hierarchical sheet has at least one matching label inside the sheet for each * port on the parent sheet object * * @param aSubgraph is the subgraph to examine * @return the number of errors found */ int ercCheckHierSheets(); /** * Get the number of pins in a given subgraph * @param aLocSubgraph Subgraph to search * @return total number of pins in the subgraph */ size_t hasPins( const CONNECTION_SUBGRAPH* aLocSubgraph ); private: // All the sheets in the schematic (as long as we don't have partial updates) SCH_SHEET_LIST m_sheetList; // All connectable items in the schematic std::vector m_items; // The owner of all CONNECTION_SUBGRAPH objects std::vector m_subgraphs; // Cache of a subset of m_subgraphs std::vector m_driver_subgraphs; // Cache to lookup subgraphs in m_driver_subgraphs by sheet path std::unordered_map> m_sheet_to_subgraphs_map; std::vector> m_invisible_power_pins; std::unordered_map> m_bus_alias_cache; std::unordered_map m_net_name_to_code_map; std::unordered_map m_bus_name_to_code_map; std::unordered_map> m_global_label_cache; std::map< std::pair, std::vector > m_local_label_cache; std::unordered_map> m_net_name_to_subgraphs_map; std::unordered_map m_item_to_subgraph_map; NET_MAP m_net_code_to_subgraphs_map; int m_last_net_code; int m_last_bus_code; int m_last_subgraph_code; SCHEMATIC* m_schematic; ///< The schematic this graph represents }; #endif