| #ifndef NETLIST_H |
| #define NETLIST_H |
| |
| /* |
| * Summary |
| * ======= |
| * This file defines the Netlist class, which stores the connectivity information |
| * of the components in a netlist. It is the base class for AtomNetlist and ClusteredNetlist. |
| * |
| * Overview |
| * ======== |
| * The netlist logically consists of several different components: Blocks, Ports, Pins and Nets |
| * Each component in the netlist has a unique template identifier (BlockId, PortId, PinId, NetId) |
| * used to retrieve information about it. In this implementation these ID's are unique throughout the |
| * netlist (i.e. every port in the netlist has a unique ID, even if the ports share a common type). |
| * |
| * Block |
| * ----- |
| * A Block is the primitive netlist element (a node in the netlist hyper-graph). |
| * Blocks have various attributes (a name, a type etc.) and are associated with sets of |
| * input/output/clock ports. |
| * |
| * Block related information can be retrieved using the block_*() member functions. |
| * |
| * Pins |
| * ---- |
| * Pins define single-bit connections between a block and a net. |
| * |
| * Pin related information can be retrieved using the pin_*() member functions. |
| * |
| * Nets |
| * ---- |
| * Nets represent the connections between blocks (the edges of the netlist hyper-graph). |
| * Each net has a single driver pin, and a set of sink pins. |
| * |
| * Net related information can be retrieved using the net_*() member functions. |
| * |
| * Ports |
| * ----- |
| * A Port is a (potentially multi-bit) group of pins. |
| * |
| * For example, the two operands and output of an N-bit adder would logically be grouped as three ports. |
| * Ports have a specified bit-width which defines how many pins form the port. |
| * |
| * Port related information can be retrieved using the port_*() member functions. |
| * |
| * |
| * Usage |
| * ===== |
| * |
| * The following provides usage examples for common use-cases. |
| * |
| * Walking the netlist |
| * ------------------- |
| * To iterate over the whole netlist use the blocks() and/or nets() member functions: |
| * |
| * Netlist netlist; |
| * |
| * //... initialize the netlist |
| * |
| * //Iterate over all the blocks |
| * for(BlockId blk_id : netlist.blocks()) { |
| * //Do something with each block |
| * } |
| * |
| * |
| * //Iterate over all the nets |
| * for(NetId net_id : netlist.nets()) { |
| * //Do something with each net |
| * } |
| * |
| * To retrieve information about a netlist component call one of the associated member functions: |
| * |
| * //Print out each block's name |
| * for(BlockId blk_id : netlist.blocks()) { |
| * |
| * //Get the block name |
| * const std::string& block_name = netlist.block_name(blk_id); |
| * |
| * //Print it |
| * printf("Block: %s\n", block_name.c_str()); |
| * } |
| * |
| * Note that the member functions are associated with the type of component (e.g. block_name() yields |
| * the name of a block, net_name() yields the name of a net). |
| * |
| * Tracing cross-references |
| * ------------------------ |
| * It is common to need to trace the netlist connectivity. The Netlist allows this to be done |
| * efficiently by maintaining cross-references between the various netlist components. |
| * |
| * The following diagram shows the main methods and relationships between netlist components: |
| * |
| * +---------+ pin_block() |
| * | |<--------------------------+ |
| * | Block | | |
| * | |-----------------------+ | |
| * +---------+ block_pins() | | |
| * | ^ v | |
| * | | +---------+ net_pins() +---------+ |
| * | | | |<-------------| | |
| * block_ports() | | port_block() | Pin | | Net | |
| * | | | |------------->| | |
| * | | +---------+ pin_net() +---------+ |
| * v | ^ | |
| * +---------+ port_pins() | | |
| * | |-----------------------+ | |
| * | Port | | |
| * | |<--------------------------+ |
| * +---------+ pin_port() |
| * |
| * Note that methods which are plurals (e.g. net_pins()) return multiple components. |
| * |
| * |
| * As an example consider the case where we wish to find all the blocks associated with a particular net: |
| * |
| * NetId net_id; |
| * |
| * //... Initialize net_id with the net of interest |
| * |
| * //Iterate through each pin on the net to get the associated port |
| * for(PinId pin_id : netlist.net_pins(net_id)) { |
| * |
| * //Get the port associated with the pin |
| * PortId port_id = netlist.pin_port(pin_id); |
| * |
| * //Get the block associated with the port |
| * BlockId blk_id = netlist.port_block(port_id); |
| * |
| * //Print out the block name |
| * const std::string& block_name = netlist.block_name(blk_id); |
| * printf("Associated block: %s\n", block_name.c_str()); |
| * } |
| * |
| * Netlist also defines some convenience functions for common operations to avoid tracking the intermediate IDs |
| * if they are not needed. The following produces the same result as above: |
| * |
| * NetId net_id; |
| * |
| * //... Initialize net_id with the net of interest |
| * |
| * //Iterate through each pin on the net to get the associated port |
| * for(PinId pin_id : netlist.net_pins(net_id)) { |
| * |
| * //Get the block associated with the pin (bypassing the port) |
| * BlockId blk_id = netlist.pin_block(pin_id); |
| * |
| * //Print out the block name |
| * const std::string& block_name = netlist.block_name(blk_id); |
| * printf("Associated block: %s\n", block_name.c_str()); |
| * } |
| * |
| * |
| * As another example, consider the inverse problem of identifying the nets connected as inputs to a particular block: |
| * |
| * BlkId blk_id; |
| * |
| * //... Initialize blk_id with the block of interest |
| * |
| * //Iterate through the ports |
| * for(PortId port_id : netlist.block_input_ports(blk_id)) { |
| * |
| * //Iterate through the pins |
| * for(PinId pin_id : netlist.port_pins(port_id)) { |
| * //Retrieve the net |
| * NetId net_id = netlist.pin_net(pin_id); |
| * |
| * //Get its name |
| * const std::string& net_name = netlist.net_name(net_id); |
| * printf("Associated net: %s\n", net_name.c_str()); |
| * } |
| * } |
| * |
| * Here we used the block_input_ports() method which returned an iterable range of all the input ports |
| * associated with blk_id. We then used the port_pins() method to get iterable ranges of all the pins |
| * associated with each port, from which we can find the associated net. |
| * |
| * Often port information is not relevant so this can be further simplified by iterating over a block's pins |
| * directly (e.g. by calling one of the block_*_pins() functions): |
| * |
| * BlkId blk_id; |
| * |
| * //... Initialize blk_id with the block of interest |
| * |
| * //Iterate over the blocks ports directly |
| * for(PinId pin_id : netlist.block_input_pins(blk_id)) { |
| * |
| * //Retrieve the net |
| * NetId net_id = netlist.pin_net(pin_id); |
| * |
| * //Get its name |
| * const std::string& net_name = netlist.net_name(net_id); |
| * printf("Associated net: %s\n", net_name.c_str()); |
| * } |
| * |
| * Note the use of range-based-for loops in the above examples; it could also have written (more verbosely) |
| * using a conventional for loop and explicit iterators as follows: |
| * |
| * BlkId blk_id; |
| * |
| * //... Initialize blk_id with the block of interest |
| * |
| * //Iterate over the blocks ports directly |
| * auto pins = netlist.block_input_pins(blk_id); |
| * for(auto pin_iter = pins.begin(); pin_iter != pins.end(); ++pin_iter) { |
| * |
| * //Retrieve the net |
| * NetId net_id = netlist.pin_net(*pin_iter); |
| * |
| * //Get its name |
| * const std::string& net_name = netlist.net_name(net_id); |
| * printf("Associated net: %s\n", net_name.c_str()); |
| * } |
| * |
| * |
| * Creating the netlist |
| * -------------------- |
| * The netlist can be created by using the create_*() member functions to create individual Blocks/Ports/Pins/Nets. |
| * |
| * For instance to create the following netlist (where each block is the same type, and has an input port 'A' |
| * and output port 'B'): |
| * |
| * ----------- net1 ----------- |
| * | block_1 |-------------------->| block_2 | |
| * ----------- | ----------- |
| * | |
| * | ----------- |
| * ---------->| block_3 | |
| * ----------- |
| * We could do the following: |
| * |
| * const t_model* blk_model = .... //Initialize the block model appropriately |
| * |
| * Netlist netlist("my_netlist"); //Initialize the netlist with name 'my_netlist' |
| * |
| * //Create the first block |
| * BlockId blk1 = netlist.create_block("block_1", blk_model); |
| * |
| * //Create the first block's output port |
| * // Note that the input/output/clock type of the port is determined |
| * // automatically from the block model |
| * PortId blk1_out = netlist.create_port(blk1, "B"); |
| * |
| * //Create the net |
| * NetId net1 = netlist.create_net("net1"); |
| * |
| * //Associate the net with blk1 |
| * netlist.create_pin(blk1_out, 0, net1, PinType::DRIVER); |
| * |
| * //Create block 2 and hook it up to net1 |
| * BlockId blk2 = netlist.create_block("block_2", blk_model); |
| * PortId blk2_in = netlist.create_port(blk2, "A"); |
| * netlist.create_pin(blk2_in, 0, net1, PinType::SINK); |
| * |
| * //Create block 3 and hook it up to net1 |
| * BlockId blk3 = netlist.create_block("block_3", blk_model); |
| * PortId blk3_in = netlist.create_port(blk3, "A"); |
| * netlist.create_pin(blk3_in, 0, net1, PinType::SINK); |
| * |
| * Modifying the netlist |
| * --------------------- |
| * The netlist can also be modified by using the remove_*() member functions. If we wanted to remove |
| * block_3 from the netlist creation example above we could do the following: |
| * |
| * //Mark blk3 and any references to it invalid |
| * netlist.remove_block(blk3); |
| * |
| * //Compress the netlist to actually remove the data associated with blk3 |
| * // NOTE: This will invalidate all client held IDs (e.g. blk1, blk1_out, net1, blk2, blk2_in) |
| * netlist.compress(); |
| * |
| * The resulting netlist connectivity now looks like: |
| * |
| * ----------- net1 ----------- |
| * | block_1 |-------------------->| block_2 | |
| * ----------- ----------- |
| * |
| * Note that until compress() is called any 'removed' elements will have invalid IDs (e.g. BlockId::INVALID()). |
| * As a result after calling remove_block() (which invalidates blk3) we *then* called compress() to remove the |
| * invalid IDs. |
| * |
| * Also note that compress() is relatively slow. As a result avoid calling compress() after every call to |
| * a remove_*() function, and instead batch up calls to remove_*() and call compress() only after a set of |
| * modifications have been applied. |
| * |
| * Verifying the netlist |
| * --------------------- |
| * Particularly after construction and/or modification it is a good idea to check that the netlist is in |
| * a valid and consistent state. This can be done with the verify() member function: |
| * |
| * netlist.verify() |
| * |
| * If the netlist is not valid verify() will throw an exception, otherwise it returns true. |
| * |
| * Invariants |
| * ========== |
| * The Netlist maintains stronger invariants if the netlist is in compressed form. |
| * |
| * Netlist is compressed ('not dirty') |
| * ----------------------------------- |
| * If the netlist is compressed (i.e. !is_dirty(), meaning there have been NO calls to remove_*() since the last call to |
| * compress()) the following invariant will hold: |
| * - Any range returned will contain only valid IDs |
| * |
| * In practise this means the following conditions hold: |
| * - Blocks will not contain empty ports/pins (e.g. ports with no pin/net connections) |
| * - Ports will not contain pins with no associated net |
| * - Nets will not contain invalid sink pins |
| * |
| * This means that no error checking for invalid IDs is needed if simply iterating through netlist (see below for |
| * some exceptions). |
| * |
| * NOTE: you may still encounter invalid IDs in the following cases: |
| * - net_driver() will return an invalid ID if the net is undriven |
| * - port_pin()/port_net() will return an invalid ID if the bit index corresponds to an unconnected pin |
| * |
| * Netlist is NOT compressed ('dirty') |
| * ----------------------------------- |
| * If the netlist is not compressed (i.e. is_dirty(), meaning there have been calls to remove_*() with no subsequent |
| * calls to compress()) then the invariant above does not hold. |
| * |
| * Any range may return invalid IDs. In practise this means, |
| * - Blocks may contain invalid ports/pins |
| * - Ports may contain invalid pins |
| * - Pins may not have a valid associated net |
| * - Nets may contain invalid sink pins |
| * |
| * Implementation Details |
| * ====================== |
| * The netlist is stored in Struct-of-Arrays format rather than the more conventional Array-of-Structs. |
| * This improves cache locality by keeping component attributes of the same type in contiguous memory. |
| * This prevents unneeded member data from being pulled into the cache (since most code accesses only a few |
| * attributes at a time this tends to be more efficient). |
| * |
| * Clients of this class pass nearly-opaque IDs (BlockId, PortId, PinId, NetId, StringId) to retrieve |
| * information. The ID is internally converted to an index to retrieve the required value from it's associated storage. |
| * |
| * By using nearly-opaque IDs we can change the underlying data layout as need to optimize performance/memory, without |
| * disrupting client code. |
| * |
| * Strings |
| * ------- |
| * To minimize memory usage, we store each unique string only once in the netlist and give it a unique ID (StringId). |
| * Any references to this string then make use of the StringId. |
| * |
| * In particular this prevents the (potentially large) strings from begin duplicated multiple times in various look-ups, |
| * instead the more space efficient StringId is duplicated. |
| * |
| * Note that StringId is an internal implementation detail and should not be exposed as part of the public interface. |
| * Any public functions should take and return std::string's instead. |
| * |
| * Block pins/Block ports data layout |
| * ---------------------------------- |
| * The pins/ports for each block are stored in a similar manner, for brevity we describe only pins here. |
| * |
| * The pins for each block (i.e. PinId's) are stored in a single vector for each block (the block_pins_ member). |
| * This allows us to iterate over all pins (i.e. block_pins()), or specific subsets of pins (e.g. only inputs with |
| * block_input_pins()). |
| * |
| * To accomplish this all pins of the same group (input/output/clock) are located next to each other. An example is |
| * shown below, where the block has n input pins, m output pins and k clock pins. |
| * |
| * ------------------------------------------------------------------------------------------------------------------- |
| * | ipin_1 | ipin_2 | ... | ipin_n | opin_1 | opin_2 | ... | opin_m | clock_pin_1 | clock_pin_2 | ... | clock_pin_k | |
| * ------------------------------------------------------------------------------------------------------------------- |
| * ^ ^ ^ ^ |
| * | | | | |
| * begin opin_begin clock_pin_begin end |
| * |
| * Provided we know the internal dividing points (i.e. opin_begin and clock_pin_begin) we can easily build the various |
| * ranges of interest: |
| * |
| * all pins : [begin, end) |
| * input pins : [begin, opin_begin) |
| * output pins: [opin_begin, clock_pin_begin) |
| * clock pins : [clock_pin_begin, end) |
| * |
| * Since any reallocation would invalidate any iterators to these internal dividers, we separately store the number |
| * of input/output/clock pins per block (i.e. in block_num_input_pins_, block_num_output_pins_ and |
| * block_num_clock_pins_). The internal dividers can then be easily calculated (e.g. see block_output_pins()), even |
| * if new pins are inserted (provided the counts are updated). |
| * |
| * Adding data to the netlist |
| * -------------------------- |
| * The Netlist should contain only information directly related to the netlist state (i.e. netlist connectivity). |
| * Various mappings to/from elements (e.g. what CLB contains an atom block), and algorithmic state (e.g. if a net |
| * is routed) do NOT constitute netlist state and should NOT be stored here. |
| * |
| * Such implementation state should be stored in other data structures (which may reference the Netlist's IDs). |
| * |
| * The netlist state should be immutable (i.e. read-only) for most of the CAD flow. |
| * |
| * Interactions with other netlists |
| * -------------------------------- |
| * Currently, the AtomNetlist and ClusteredNetlist are both derived from Netlist. The AtomNetlist has primitive |
| * specific details (t_model, TruthTable), and handles all operations with the atoms. The ClusteredNetlist |
| * contains information on the CLB (Clustered Logic Block) level, which includes the physical description of the |
| * blocks (t_logical_block_type), as well as the internal hierarchy and wiring (t_pb/t_pb_route). |
| * |
| * The calling-conventions of the functions in the AtomNetlist and ClusteredNetlist is as follows: |
| * |
| * Functions where the derived class (Atom/Clustered) calls the base class (Netlist) |
| * create_*() |
| * |
| * Functions where the base class calls the derived class (Non-Virtual Interface idiom as described |
| * https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface) |
| * remove_*() |
| * clean_*() |
| * validate_*_sizes() |
| * shrink_to_fit() |
| * The derived functions based off of the virtual functions have suffix *_impl() |
| * |
| */ |
| #include <string> |
| #include <vector> |
| #include <unordered_map> |
| #include "vtr_range.h" |
| #include "vtr_logic.h" |
| #include "vtr_vector_map.h" |
| |
| #include "logic_types.h" |
| |
| #include "netlist_fwd.h" |
| #include "netlist_utils.h" |
| |
| //Forward declaration for private methods |
| template<typename BlockId, typename PortId, typename PinId, typename NetId> |
| class NetlistIdRemapper { |
| public: |
| BlockId new_block_id(BlockId old_blk) const; |
| PortId new_port_id(PortId old_port) const; |
| PinId new_pin_id(PinId old_pin) const; |
| NetId new_net_id(NetId old_net) const; |
| |
| private: |
| friend Netlist<BlockId, PortId, PinId, NetId>; |
| |
| vtr::vector_map<BlockId, BlockId> block_id_map_; |
| vtr::vector_map<PortId, PortId> port_id_map_; |
| vtr::vector_map<PinId, PinId> pin_id_map_; |
| vtr::vector_map<NetId, NetId> net_id_map_; |
| }; |
| |
| template<typename BlockId, typename PortId, typename PinId, typename NetId> |
| class Netlist { |
| public: //Public Types |
| typedef typename vtr::vector_map<BlockId, BlockId>::const_iterator block_iterator; |
| typedef typename std::unordered_map<std::string, std::string>::const_iterator attr_iterator; |
| typedef typename std::unordered_map<std::string, std::string>::const_iterator param_iterator; |
| typedef typename vtr::vector_map<NetId, NetId>::const_iterator net_iterator; |
| typedef typename vtr::vector_map<PinId, PinId>::const_iterator pin_iterator; |
| typedef typename vtr::vector_map<PortId, PortId>::const_iterator port_iterator; |
| |
| typedef typename vtr::Range<block_iterator> block_range; |
| typedef typename vtr::Range<attr_iterator> attr_range; |
| typedef typename vtr::Range<param_iterator> param_range; |
| typedef typename vtr::Range<net_iterator> net_range; |
| typedef typename vtr::Range<pin_iterator> pin_range; |
| typedef typename vtr::Range<port_iterator> port_range; |
| |
| typedef NetlistIdRemapper<BlockId, PortId, PinId, NetId> IdRemapper; |
| |
| public: |
| Netlist(std::string name = "", std::string id = ""); |
| virtual ~Netlist(); |
| |
| public: //Public Accessors |
| /* |
| * Netlist |
| */ |
| //Retrieve the name of the netlist |
| const std::string& netlist_name() const; |
| |
| //Retrieve the unique identifier for this netlist |
| //This is typically a secure digest of the input file. |
| const std::string& netlist_id() const; |
| |
| /* |
| * Utility |
| */ |
| //Sanity check for internal consistency (throws an exception on failure) |
| bool verify() const; |
| |
| //Returns true if the netlist has invalid entries due to modifications (e.g. from remove_*() calls) |
| bool is_dirty() const; |
| |
| //Returns true if the netlist has *no* invalid entries due to modifications (e.g. from remove_*() calls) |
| //Note that this is a convenience method which is the logical inverse of is_dirty() |
| bool is_compressed() const; |
| |
| //Item counts and container info (for debugging) |
| void print_stats() const; |
| |
| /* |
| * Blocks |
| */ |
| //Returns the name of the specified block |
| const std::string& block_name(const BlockId blk_id) const; |
| |
| //Returns true if the block is purely combinational (i.e. no input clocks |
| //and not a primary input |
| bool block_is_combinational(const BlockId blk_id) const; |
| |
| // Returns a range of all attributes associated with the specified block |
| attr_range block_attrs(const BlockId blk_id) const; |
| |
| // Returns a range of all parameters associated with the specified block |
| param_range block_params(const BlockId blk_id) const; |
| |
| //Returns a range of all pins associated with the specified block |
| pin_range block_pins(const BlockId blk_id) const; |
| |
| //Returns a range of all input pins associated with the specified block |
| pin_range block_input_pins(const BlockId blk_id) const; |
| |
| //Returns a range of all output pins associated with the specified block |
| // Note this is typically only data pins, but some blocks (e.g. PLLs) can produce outputs |
| // which are clocks. |
| pin_range block_output_pins(const BlockId blk_id) const; |
| |
| //Returns a range of all clock pins associated with the specified block |
| pin_range block_clock_pins(const BlockId blk_id) const; |
| |
| //Returns a range of all ports associated with the specified block |
| port_range block_ports(const BlockId blk_id) const; |
| |
| //Returns a range consisting of the input ports associated with the specified block |
| port_range block_input_ports(const BlockId blk_id) const; |
| |
| //Returns a range consisting of the output ports associated with the specified block |
| // Note this is typically only data ports, but some blocks (e.g. PLLs) can produce outputs |
| // which are clocks. |
| port_range block_output_ports(const BlockId blk_id) const; |
| |
| //Returns a range consisting of the input clock ports associated with the specified block |
| port_range block_clock_ports(const BlockId blk_id) const; |
| |
| //Removes a block from the netlist. This will also remove the associated ports and pins. |
| // blk_id : The block to be removed |
| void remove_block(const BlockId blk_id); |
| |
| /* |
| * Ports |
| */ |
| //Returns the name of the specified port |
| const std::string& port_name(const PortId port_id) const; |
| |
| //Returns the block associated with the specified port |
| BlockId port_block(const PortId port_id) const; |
| |
| //Returns the set of valid pins associated with the port |
| pin_range port_pins(const PortId port_id) const; |
| |
| //Returns the pin (potentially invalid) associated with the specified port and port bit index |
| // port_id : The ID of the associated port |
| // port_bit: The bit index of the pin in the port |
| //Note: this function is a synonym for find_pin() |
| PinId port_pin(const PortId port_id, const BitIndex port_bit) const; |
| |
| //Returns the net (potentially invalid) associated with the specified port and port bit index |
| // port_id : The ID of the associated port |
| // port_bit: The bit index of the pin in the port |
| NetId port_net(const PortId port_id, const BitIndex port_bit) const; |
| |
| //Returns the width (number of bits) in the specified port |
| BitIndex port_width(const PortId port_id) const; |
| |
| //Returns the type of the specified port |
| PortType port_type(const PortId port_id) const; |
| |
| //Removes a port from the netlist. |
| //The port's pins are also marked invalid and removed from any associated nets |
| // port_id: The ID of the port to be removed |
| void remove_port(const PortId port_id); |
| |
| /* |
| * Pins |
| */ |
| //Returns the constructed name (derived from block and port) for the specified pin |
| std::string pin_name(const PinId pin_id) const; |
| |
| //Returns the type of the specified pin |
| PinType pin_type(const PinId pin_id) const; |
| |
| //Returns the net associated with the specified pin |
| NetId pin_net(const PinId pin_id) const; |
| |
| //Returns the index of the specified pin within it's connected net |
| int pin_net_index(const PinId pin_id) const; |
| |
| //Returns the port associated with the specified pin |
| PortId pin_port(const PinId pin_id) const; |
| |
| //Returns the port bit index associated with the specified pin |
| BitIndex pin_port_bit(const PinId pin_id) const; |
| |
| //Returns the block associated with the specified pin |
| BlockId pin_block(const PinId pin_id) const; |
| |
| //Returns the port type associated with the specified pin |
| PortType pin_port_type(const PinId pin_id) const; |
| |
| //Returns true if the pin is a constant (i.e. its value never changes) |
| bool pin_is_constant(const PinId pin_id) const; |
| |
| //Removes a pin from the netlist. |
| //The pin is marked invalid, and removed from any assoicated nets |
| // pin_id: The pin_id of the pin to be removed |
| void remove_pin(const PinId pin_id); |
| |
| /* |
| * Nets |
| */ |
| //Returns the name of the specified net |
| const std::string& net_name(const NetId net_id) const; |
| |
| //Returns a range consisting of all the pins in the net (driver and sinks) |
| //The first element in the range is the driver (and may be invalid) |
| //The remaining elements (potentially none) are the sinks |
| pin_range net_pins(const NetId net_id) const; |
| |
| //Returns the net_pin_index'th pin of the specified net |
| PinId net_pin(const NetId net_id, int net_pin_index) const; |
| |
| //Returns the block associated with the net_pin_index'th pin of the specified net |
| BlockId net_pin_block(const NetId net_id, int net_pin_index) const; |
| |
| //Returns the (potentially invalid) net driver pin |
| PinId net_driver(const NetId net_id) const; |
| |
| //Returns the (potentially invalid) net driver block |
| BlockId net_driver_block(const NetId net_id) const; |
| |
| //Returns a (potentially empty) range consisting of net's sink pins |
| pin_range net_sinks(const NetId net_id) const; |
| |
| //Returns true if the net is driven by a constant pin (i.e. its value never changes) |
| bool net_is_constant(const NetId net_id) const; |
| |
| //Removes a net from the netlist. |
| //This will mark the net's pins as having no associated. |
| // net_id : The net to be removed |
| void remove_net(const NetId net_id); |
| |
| //Removes a connection betwen a net and pin. The pin is removed from the net and the pin |
| //will be marked as having no associated net |
| // net_id : The net from which the pin is to be removed |
| // pin_id : The pin to be removed from the net |
| void remove_net_pin(const NetId net_id, const PinId pin_id); |
| |
| /* |
| * Aggregates |
| */ |
| //Returns a range consisting of all blocks in the netlist |
| block_range blocks() const; |
| |
| //Returns a range consisting of all ports in the netlist |
| port_range ports() const; |
| |
| //Returns a range consisting of all nets in the netlist |
| net_range nets() const; |
| |
| //Returns a range consisting of all pins in the netlist |
| pin_range pins() const; |
| |
| /* |
| * ID Checks |
| * |
| * Validates that the specified ID is valid in the current netlist state |
| */ |
| bool valid_block_id(BlockId block_id) const; |
| bool valid_port_id(PortId port_id) const; |
| bool valid_port_bit(PortId port_id, BitIndex port_bit) const; |
| bool valid_pin_id(PinId pin_id) const; |
| bool valid_net_id(NetId net_id) const; |
| |
| /* |
| * Lookups |
| */ |
| //Returns the BlockId of the specified block or BlockId::INVALID() if not found |
| // name: The name of the block |
| BlockId find_block(const std::string& name) const; |
| |
| //Returns the PortId of the specifed port if it exists or PortId::INVALID() if not |
| //Note that this method is typically less efficient than searching by a t_model_port |
| //With the overloaded AtomNetlist method |
| // blk_id: The ID of the block who's ports will be checked |
| // name : The name of the port to look for |
| PortId find_port(const BlockId blk_id, const std::string& name) const; |
| |
| //Returns the NetId of the specified net or NetId::INVALID() if not found |
| // name: The name of the net |
| NetId find_net(const std::string& name) const; |
| |
| //Returns the PinId of the specified pin or PinId::INVALID() if not found |
| // port_id : The ID of the associated port |
| // port_bit: The bit index of the pin in the port |
| PinId find_pin(const PortId port_id, BitIndex port_bit) const; |
| |
| public: //Public Mutators |
| //Add the specified pin to the specified net as pin_type. Automatically removes |
| //any previous net connection for this pin. |
| // pin : The pin to add |
| // pin_type : The type of the pin (i.e. driver or sink) |
| // net : The net to add the pin to |
| void set_pin_net(const PinId pin, PinType pin_type, const NetId net); |
| |
| //Mark a pin as being a constant generator. |
| // There are some cases where a pin can not be identified as a is constant until after |
| // the full netlist has been built; so we expose a way to mark existing pins as constants. |
| // pin_id : The pin to be marked |
| // value : The boolean value to set the pin_is_constant attribute |
| void set_pin_is_constant(const PinId pin_id, const bool value); |
| |
| //Re-name a block |
| // blk_id : The block to be renamed |
| // new_name : The new name for the specified block |
| void set_block_name(const BlockId blk_id, const std::string new_name); |
| |
| //Set a block attribute |
| // blk_id : The block to which the attribute is attached |
| // name : The name of the attribute to set |
| // value : The new value for the specified attribute on the specified block |
| void set_block_attr(const BlockId blk_id, const std::string& name, const std::string& value); |
| |
| //Set a block parameter |
| // blk_id : The block to which the parameter is attached |
| // name : The name of the parameter to set |
| // value : The new value for the specified parameter on the specified block |
| void set_block_param(const BlockId blk_id, const std::string& name, const std::string& value); |
| |
| //Merges sink_net into driver_net |
| //After merging driver_net will contain all the sinks of sink_net |
| // driver_net: The net which includes the driver pin |
| // sink_net: The target net to be merged into driver_net (must have no driver pin) |
| void merge_nets(const NetId driver_net, const NetId sink_net); |
| |
| /* |
| * Note: all remove_*() will mark the associated items as invalid, but the items |
| * will not be removed until compress() is called. |
| */ |
| //Wrapper for remove_unused() & compress() |
| // This function should be used in the case where a netlist is fully modified |
| IdRemapper remove_and_compress(); |
| |
| //This should be called after completing a series of netlist modifications |
| //(e.g. removing blocks/ports/pins/nets). |
| // |
| //Marks netlist components which have become redundant due to other removals |
| //(e.g. ports with only invalid pins) as invalid so they will be destroyed during |
| //compress() |
| void remove_unused(); |
| |
| //Compresses the netlist, removing any invalid and/or unreferenced |
| //blocks/ports/pins/nets. |
| // |
| //NOTE: this invalidates all existing IDs! |
| IdRemapper compress(); |
| |
| protected: //Protected Mutators |
| //Create or return an existing block in the netlist |
| // name : The unique name of the block |
| BlockId create_block(const std::string name); |
| |
| //Create or return an existing port in the netlist |
| // blk_id : The block the port is associated with |
| // name : The name of the port (must match the name of a port in the block's model) |
| // width : The width (number of bits) of the port |
| // type : The type of the port (INPUT, OUTPUT, CLOCK) |
| PortId create_port(const BlockId blk_id, const std::string name, BitIndex width, PortType type); |
| |
| //Create or return an existing pin in the netlist |
| // port_id : The port this pin is associated with |
| // port_bit : The bit index of the pin in the port |
| // net_id : The net the pin drives/sinks |
| // pin_type : The type of the pin (driver/sink) |
| // is_const : Indicates whether the pin holds a constant value (e. g. vcc/gnd) |
| PinId create_pin(const PortId port_id, BitIndex port_bit, const NetId net_id, const PinType pin_type, bool is_const = false); |
| |
| //Create an empty, or return an existing net in the netlist |
| // name : The unique name of the net |
| NetId create_net(const std::string name); //An empty or existing net |
| |
| //Create a completely specified net from specified driver and sinks |
| // name : The name of the net (Note: must not already exist) |
| // driver : The net's driver pin |
| // sinks : The net's sink pins |
| NetId add_net(const std::string name, PinId driver, std::vector<PinId> sinks); |
| |
| protected: //Protected Base Types |
| struct string_id_tag; |
| |
| //A unique identifier for a string in the netlist |
| typedef vtr::StrongId<string_id_tag> StringId; |
| |
| protected: //Protected Base Members |
| /* |
| * Lookups |
| */ |
| //Returns the StringId of the specifed string if it exists or StringId::INVALID() if not |
| // str : The string to look for |
| StringId find_string(const std::string& str) const; |
| |
| //Returns the BlockId of the specifed block if it exists or BlockId::INVALID() if not |
| // name_id : The block name to look for |
| BlockId find_block(const StringId name_id) const; |
| |
| //Returns the NetId of the specifed port if it exists or NetId::INVALID() if not |
| // name_id: The string ID of the net name to look for |
| NetId find_net(const StringId name_id) const; |
| |
| /* |
| * Mutators |
| */ |
| //Create or return the ID of the specified string |
| // str: The string whose ID is requested |
| StringId create_string(const std::string& str); |
| |
| //Updates net cross-references for the specified pin |
| //Returns the pin's index within the net |
| int associate_pin_with_net(const PinId pin_id, const PinType type, const NetId net_id); |
| |
| //Updates port cross-references for the specified pin |
| void associate_pin_with_port(const PinId pin_id, const PortId port_id); |
| |
| //Updates block cross-references for the specified pin |
| void associate_pin_with_block(const PinId pin_id, const PortType type, const BlockId blk_id); |
| |
| //Updates block cross-references for the specified port |
| void associate_port_with_block(const PortId port_id, const PortType type, const BlockId blk_id); |
| |
| /* |
| * Netlist compression/optimization |
| */ |
| //Builds the new mappings from old to new IDs. |
| //The various IdMap's should be initialized with invalid mappings |
| //for all current ID's before being called. |
| IdRemapper build_id_maps(); |
| |
| //Removes invalid and reorders blocks |
| void clean_blocks(const vtr::vector_map<BlockId, BlockId>& block_id_map); |
| |
| //Removes invalid and reorders ports |
| void clean_ports(const vtr::vector_map<PortId, PortId>& port_id_map); |
| |
| //Removes invalid and reorders pins |
| void clean_pins(const vtr::vector_map<PinId, PinId>& pin_id_map); |
| |
| //Removes invalid and reorders nets |
| void clean_nets(const vtr::vector_map<NetId, NetId>& net_id_map); |
| |
| //Re-builds fast look-ups |
| void rebuild_lookups(); |
| |
| //Re-builds cross-references held by blocks |
| void rebuild_block_refs(const vtr::vector_map<PinId, PinId>& pin_id_map, |
| const vtr::vector_map<PortId, PortId>& port_id_map); |
| |
| //Re-builds cross-references held by ports |
| void rebuild_port_refs(const vtr::vector_map<BlockId, BlockId>& block_id_map, |
| const vtr::vector_map<PinId, PinId>& pin_id_map); |
| |
| //Re-builds cross-references held by pins |
| void rebuild_pin_refs(const vtr::vector_map<PortId, PortId>& port_id_map, |
| const vtr::vector_map<NetId, NetId>& net_id_map); |
| |
| //Re-builds cross-references held by nets |
| void rebuild_net_refs(const vtr::vector_map<PinId, PinId>& pin_id_map); |
| |
| void shrink_to_fit(); |
| |
| /* |
| * Sanity Checks |
| */ |
| //Verify the internal data structure sizes match |
| bool verify_sizes() const; |
| bool validate_block_sizes() const; |
| bool validate_port_sizes() const; |
| bool validate_pin_sizes() const; |
| bool validate_net_sizes() const; |
| bool validate_string_sizes() const; |
| |
| //Verify that internal data structure cross-references are consistent |
| bool verify_refs() const; //All cross-references |
| bool validate_block_port_refs() const; |
| bool validate_block_pin_refs() const; |
| bool validate_port_pin_refs() const; |
| bool validate_net_pin_refs() const; |
| bool validate_string_refs() const; |
| |
| //Verify that block invariants hold (i.e. logical consistency) |
| bool verify_block_invariants() const; |
| |
| //Verify that fast-lookups are consistent with internal data structures |
| bool verify_lookups() const; |
| |
| //Validates that the specified ID is valid in the current netlist state |
| bool valid_string_id(StringId string_id) const; |
| |
| protected: //Protected virtual functions implemented in derived classes |
| //The functions follow the Non-Virtual Interface (NVI) idiom, and |
| //are called from this class in their respective non-impl() functions. |
| virtual void shrink_to_fit_impl() = 0; |
| |
| virtual bool validate_block_sizes_impl(size_t num_blocks) const = 0; |
| virtual bool validate_port_sizes_impl(size_t num_ports) const = 0; |
| virtual bool validate_pin_sizes_impl(size_t num_pins) const = 0; |
| virtual bool validate_net_sizes_impl(size_t num_nets) const = 0; |
| |
| virtual void clean_blocks_impl(const vtr::vector_map<BlockId, BlockId>& block_id_map) = 0; |
| virtual void clean_ports_impl(const vtr::vector_map<PortId, PortId>& port_id_map) = 0; |
| virtual void clean_pins_impl(const vtr::vector_map<PinId, PinId>& pin_id_map) = 0; |
| virtual void clean_nets_impl(const vtr::vector_map<NetId, NetId>& net_id_map) = 0; |
| |
| virtual void remove_block_impl(const BlockId blk_id) = 0; |
| virtual void remove_port_impl(const PortId port_id) = 0; |
| virtual void remove_pin_impl(const PinId pin_id) = 0; |
| virtual void remove_net_impl(const NetId net_id) = 0; |
| |
| virtual void rebuild_block_refs_impl(const vtr::vector_map<PinId, PinId>& pin_id_map, const vtr::vector_map<PortId, PortId>& port_id_map) = 0; |
| virtual void rebuild_port_refs_impl(const vtr::vector_map<BlockId, BlockId>& block_id_map, const vtr::vector_map<PinId, PinId>& pin_id_map) = 0; |
| virtual void rebuild_pin_refs_impl(const vtr::vector_map<PortId, PortId>& port_id_map, const vtr::vector_map<NetId, NetId>& net_id_map) = 0; |
| virtual void rebuild_net_refs_impl(const vtr::vector_map<PinId, PinId>& pin_id_map) = 0; |
| |
| protected: |
| constexpr static int INVALID_INDEX = -1; |
| constexpr static int NET_DRIVER_INDEX = 0; |
| |
| private: //Data |
| std::string netlist_name_; //Name of the top-level netlist |
| std::string netlist_id_; //Unique identifier for the netlist |
| bool dirty_ = false; //Indicates the netlist has invalid entries from remove_*() functions |
| |
| //Block data |
| vtr::vector_map<BlockId, BlockId> block_ids_; //Valid block ids |
| vtr::vector_map<BlockId, StringId> block_names_; //Name of each block |
| |
| vtr::vector_map<BlockId, std::vector<PortId>> block_ports_; //Ports of each block |
| vtr::vector_map<BlockId, unsigned> block_num_input_ports_; //Input ports of each block |
| vtr::vector_map<BlockId, unsigned> block_num_output_ports_; //Output ports of each block |
| vtr::vector_map<BlockId, unsigned> block_num_clock_ports_; //Clock ports of each block |
| |
| vtr::vector_map<BlockId, std::vector<PinId>> block_pins_; //Pins of each block |
| vtr::vector_map<BlockId, unsigned> block_num_input_pins_; //Number of input pins on each block |
| vtr::vector_map<BlockId, unsigned> block_num_output_pins_; //Number of output pins on each block |
| vtr::vector_map<BlockId, unsigned> block_num_clock_pins_; //Number of clock pins on each block |
| |
| vtr::vector_map<BlockId, std::unordered_map<std::string, std::string>> block_params_; //Parameters of each block |
| vtr::vector_map<BlockId, std::unordered_map<std::string, std::string>> block_attrs_; //Attributes of each block |
| |
| //Port data |
| vtr::vector_map<PortId, PortId> port_ids_; //Valid port ids |
| vtr::vector_map<PortId, StringId> port_names_; //Name of each port |
| vtr::vector_map<PortId, BlockId> port_blocks_; //Block associated with each port |
| vtr::vector_map<PortId, std::vector<PinId>> port_pins_; //Pins associated with each port |
| vtr::vector_map<PortId, BitIndex> port_widths_; //Width (in bits) of each port |
| vtr::vector_map<PortId, PortType> port_types_; //Type of the port (INPUT, OUTPUT, CLOCK) |
| |
| //Pin data |
| vtr::vector_map<PinId, PinId> pin_ids_; //Valid pin ids |
| vtr::vector_map<PinId, PortId> pin_ports_; //Type of each pin |
| vtr::vector_map<PinId, BitIndex> pin_port_bits_; //The pins bit position in the port |
| vtr::vector_map<PinId, NetId> pin_nets_; //Net associated with each pin |
| vtr::vector_map<PinId, int> pin_net_indices_; //Index of the specified pin within it's associated net |
| vtr::vector_map<PinId, bool> pin_is_constant_; //Indicates if the pin always keeps a constant value |
| |
| //Net data |
| vtr::vector_map<NetId, NetId> net_ids_; //Valid net ids |
| vtr::vector_map<NetId, StringId> net_names_; //Name of each net |
| vtr::vector_map<NetId, std::vector<PinId>> net_pins_; //Pins associated with each net |
| |
| //String data |
| // We store each unique string once, and reference it by an StringId |
| // This avoids duplicating the strings in the fast look-ups (i.e. the look-ups |
| // only store the Ids) |
| vtr::vector_map<StringId, StringId> string_ids_; //Valid string ids |
| vtr::vector_map<StringId, std::string> strings_; //Strings |
| |
| private: //Fast lookups |
| vtr::vector_map<StringId, BlockId> block_name_to_block_id_; |
| vtr::vector_map<StringId, NetId> net_name_to_net_id_; |
| std::unordered_map<std::string, StringId> string_to_string_id_; |
| }; |
| |
| #include "netlist.tpp" |
| #endif |