blob: f7e99267c27dcd2abd94441c147a03596ecc0d8d [file] [log] [blame]
#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