move mux graph and decoder builders to vpr8 integration; ready to link the rr_switch to circuit models

This commit is contained in:
tangxifan 2020-02-11 21:02:58 -07:00
parent 175bef014a
commit 4367dba9b7
13 changed files with 2535 additions and 0 deletions

View File

@ -0,0 +1,109 @@
/***************************************************************************************
* This file includes memeber functions for data structure DecoderLibrary
**************************************************************************************/
#include "vtr_assert.h"
#include "decoder_library.h"
/* Begin namespace openfpga */
namespace openfpga {
/***************************************************************************************
* Public Accessors: Aggregators
**************************************************************************************/
DecoderLibrary::decoder_range DecoderLibrary::decoders() const {
return vtr::make_range(decoder_ids_.begin(), decoder_ids_.end());
}
/***************************************************************************************
* Public Accessors: Data query
**************************************************************************************/
/* Get the size of address input of a decoder */
size_t DecoderLibrary::addr_size(const DecoderId& decoder) const {
VTR_ASSERT_SAFE(valid_decoder_id(decoder));
return addr_sizes_[decoder];
}
/* Get the size of data output of a decoder */
size_t DecoderLibrary::data_size(const DecoderId& decoder) const {
VTR_ASSERT_SAFE(valid_decoder_id(decoder));
return data_sizes_[decoder];
}
/* Get the flag if a decoder includes an ENABLE signal */
bool DecoderLibrary::use_enable(const DecoderId& decoder) const {
VTR_ASSERT_SAFE(valid_decoder_id(decoder));
return use_enable_[decoder];
}
/* Get the flag if a decoder includes an DATA_IN signal */
bool DecoderLibrary::use_data_in(const DecoderId& decoder) const {
VTR_ASSERT_SAFE(valid_decoder_id(decoder));
return use_data_in_[decoder];
}
/* Get the flag if a decoder includes a data_inv port which is an inversion of the regular data output port */
bool DecoderLibrary::use_data_inv_port(const DecoderId& decoder) const {
VTR_ASSERT_SAFE(valid_decoder_id(decoder));
return use_data_inv_port_[decoder];
}
/* Find a decoder to the library, with the specification.
* If found, return the id of decoder.
* If not found, return an invalid id of decoder
* To avoid duplicated decoders, this function should be used before adding a decoder
* Example:
* DecoderId decoder_id == decoder_lib.find_decoder();
* if (DecoderId::INVALID() == decoder_id) {
* // Add decoder
* }
*/
DecoderId DecoderLibrary::find_decoder(const size_t& addr_size,
const size_t& data_size,
const bool& use_enable,
const bool& use_data_in,
const bool& use_data_inv_port) const {
for (auto decoder : decoders()) {
if ( (addr_size == addr_sizes_[decoder])
&& (data_size == data_sizes_[decoder])
&& (use_enable == use_enable_[decoder])
&& (use_data_in == use_data_in_[decoder])
&& (use_data_inv_port == use_data_inv_port_[decoder]) ) {
return decoder;
}
}
/* Not found, return an invalid id by default */
return DecoderId::INVALID();
}
/***************************************************************************************
* Public Validators
**************************************************************************************/
/* Validate ids */
bool DecoderLibrary::valid_decoder_id(const DecoderId& decoder) const {
return size_t(decoder) < decoder_ids_.size() && decoder_ids_[decoder] == decoder;
}
/***************************************************************************************
* Public Mutators : Basic Operations
**************************************************************************************/
/* Add a decoder to the library */
DecoderId DecoderLibrary::add_decoder(const size_t& addr_size,
const size_t& data_size,
const bool& use_enable,
const bool& use_data_in,
const bool& use_data_inv_port) {
DecoderId decoder = DecoderId(decoder_ids_.size());
/* Push to the decoder list */
decoder_ids_.push_back(decoder);
/* Resize the other related vectors */
addr_sizes_.push_back(addr_size);
data_sizes_.push_back(data_size);
use_enable_.push_back(use_enable);
use_data_in_.push_back(use_data_in);
use_data_inv_port_.push_back(use_data_inv_port);
return decoder;
}
} /* End namespace openfpga*/

View File

@ -0,0 +1,95 @@
/***************************************************************************************
* This file includes key data structures to describe decoders which are used
* in FPGA fabrics
* A decoder is a circuit to convert a binary input to one-hot codes
* The outputs are assumes to be one-hot codes (at most only one '1' exist)
* Therefore, the number of inputs is ceil(log(num_of_outputs)/log(2))
* All the decoders are assumed to follow the port map :
*
* Inputs
* | | ... |
* v v v
* +-----------+
* / \
* / Decoder \
* +-----------------+
* | | | ... | | |
* v v v v v v
* Outputs
***************************************************************************************/
#ifndef DECODER_LIBRARY_H
#define DECODER_LIBRARY_H
#include "vtr_vector.h"
#include "vtr_range.h"
#include "decoder_library_fwd.h"
/* Begin namespace openfpga */
namespace openfpga {
class DecoderLibrary {
public: /* Types and ranges */
typedef vtr::vector<DecoderId, DecoderId>::const_iterator decoder_iterator;
typedef vtr::Range<decoder_iterator> decoder_range;
public: /* Public accessors: Aggregates */
/* Get all the decoders */
decoder_range decoders() const;
public: /* Public accessors: Data query */
/* Get the size of address input of a decoder */
size_t addr_size(const DecoderId& decoder) const;
/* Get the size of data output of a decoder */
size_t data_size(const DecoderId& decoder) const;
/* Get the flag if a decoder includes an ENABLE signal */
bool use_enable(const DecoderId& decoder) const;
/* Get the flag if a decoder includes an DATA_IN signal */
bool use_data_in(const DecoderId& decoder) const;
/* Get the flag if a decoder includes a data_inv port which is an inversion of the regular data output port */
bool use_data_inv_port(const DecoderId& decoder) const;
/* Find a decoder to the library, with the specification.
* If found, return the id of decoder.
* If not found, return an invalid id of decoder
* To avoid duplicated decoders, this function should be used before adding a decoder
* Example:
* DecoderId decoder_id == decoder_lib.find_decoder();
* if (DecoderId::INVALID() == decoder_id) {
* // Add decoder
* }
*/
DecoderId find_decoder(const size_t& addr_size,
const size_t& data_size,
const bool& use_enable,
const bool& use_data_in,
const bool& use_data_inv_port) const;
public: /* Public validators */
/* valid ids */
bool valid_decoder_id(const DecoderId& decoder) const;
public: /* Private mutators : basic operations */
/* Add a decoder to the library */
DecoderId add_decoder(const size_t& addr_size,
const size_t& data_size,
const bool& use_enable,
const bool& use_data_in,
const bool& use_data_inv_port);
private: /* Internal Data */
vtr::vector<DecoderId, DecoderId> decoder_ids_;
vtr::vector<DecoderId, size_t> addr_sizes_;
vtr::vector<DecoderId, size_t> data_sizes_;
vtr::vector<DecoderId, bool> use_enable_;
vtr::vector<DecoderId, bool> use_data_in_;
vtr::vector<DecoderId, bool> use_data_inv_port_;
};
} /* End namespace openfpga*/
#endif

View File

@ -0,0 +1,23 @@
/**************************************************
* This file includes only declarations for
* the data structures to describe decoders
* Please refer to decoder_library.h for more details
*************************************************/
#ifndef DECODER_LIBRARY_FWD_H
#define DECODER_LIBRARY_FWD_H
#include "vtr_strong_id.h"
/* Begin namespace openfpga */
namespace openfpga {
/* Strong Ids for MUXes */
struct decoder_id_tag;
typedef vtr::StrongId<decoder_id_tag> DecoderId;
class DecoderLibrary;
} /* End namespace openfpga*/
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,213 @@
#ifndef MUX_GRAPH_H
#define MUX_GRAPH_H
/********************************************************************
* Include header files required by the data structure definition
*******************************************************************/
#include <map>
#include "vtr_vector.h"
#include "vtr_range.h"
#include "mux_graph_fwd.h"
#include "circuit_library.h"
/* Begin namespace openfpga */
namespace openfpga {
/**************************************************
* This file includes a data structure to describe
* the internal structure of a multiplexer
* using a generic graph representation
* A Branch is a N:1 MUX in the part of MUX graph
*
* branch_input --->+
* |
* branch_input --->|
* |--> branch_out
* ... |
* |
* branch_input --->+
*
* A short example of how a two-level MUX is organized by branches
*
* +-----------+ +--------+
* mux_inputs--->| Branch[0] |--->| |
* +-----------+ | |
* ... | Branch |---> mux_out
* +-----------+ | [N+1] |
* mux_inputs--->| Branch[N] |--->| |
* +-----------+ +--------+
*
*************************************************/
class MuxGraph {
private: /* data types used only in this class */
enum e_mux_graph_node_type {
MUX_INPUT_NODE,
MUX_INTERNAL_NODE,
MUX_OUTPUT_NODE,
NUM_MUX_NODE_TYPES
};
public: /* Types and ranges */
typedef vtr::vector<MuxNodeId, MuxNodeId>::const_iterator node_iterator;
typedef vtr::vector<MuxEdgeId, MuxEdgeId>::const_iterator edge_iterator;
typedef vtr::vector<MuxMemId, MuxMemId>::const_iterator mem_iterator;
typedef vtr::Range<node_iterator> node_range;
typedef vtr::Range<edge_iterator> edge_range;
typedef vtr::Range<mem_iterator> mem_range;
public: /* Public Constructors */
/* Create an object based on a Circuit Model which is MUX */
MuxGraph(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size);
private: /* Private Constructors*/
/* Create an empty graph */
MuxGraph();
public: /* Public accessors: Aggregates */
node_range nodes() const;
/* Find the non-input nodes */
std::vector<MuxNodeId> non_input_nodes() const;
edge_range edges() const;
mem_range memories() const;
/* Find the number of levels in terms of the multiplexer */
std::vector<size_t> levels() const;
/* Find the actual number of levels in the graph */
std::vector<size_t> node_levels() const;
public: /* Public accessors: Data query */
/* Find the number of inputs in the MUX graph */
size_t num_inputs() const;
std::vector<MuxNodeId> inputs() const;
/* Find the number of outputs in the MUX graph */
size_t num_outputs() const;
std::vector<MuxNodeId> outputs() const;
/* Find the edge between two MUX nodes */
std::vector<MuxEdgeId> find_edges(const MuxNodeId& from_node, const MuxNodeId& to_node) const;
/* Find the number of levels in the MUX graph */
size_t num_levels() const;
size_t num_node_levels() const;
/* Find the number of SRAMs in the MUX graph */
size_t num_memory_bits() const;
/* Find the number of SRAMs at a level in the MUX graph */
size_t num_memory_bits_at_level(const size_t& level) const;
/* Return memory id at level */
std::vector<MuxMemId> memories_at_level(const size_t& level) const;
/* Find the number of nodes at a given level in the MUX graph */
size_t num_nodes_at_level(const size_t& level) const;
/* Find the level of a node */
size_t node_level(const MuxNodeId& node) const;
/* Find the index of a node at its level */
size_t node_index_at_level(const MuxNodeId& node) const;
/* Find the input edges for a node */
std::vector<MuxEdgeId> node_in_edges(const MuxNodeId& node) const;
/* Find the input nodes for a edge */
std::vector<MuxNodeId> edge_src_nodes(const MuxEdgeId& edge) const;
/* Find the mem that control the edge */
MuxMemId find_edge_mem(const MuxEdgeId& edge) const;
/* Identify if the edge is controlled by the inverted output of a mem */
bool is_edge_use_inv_mem(const MuxEdgeId& edge) const;
/* Find the sizes of each branch of a MUX */
std::vector<size_t> branch_sizes() const;
/* Find the sizes of each branch of a MUX at a given level */
std::vector<size_t> branch_sizes(const size_t& level) const;
/* Generate MUX graphs for its branches */
MuxGraph subgraph(const MuxNodeId& node) const;
std::vector<MuxGraph> build_mux_branch_graphs() const;
/* Get the node id of a given input */
MuxNodeId node_id(const MuxInputId& input_id) const;
/* Get the node id of a given output */
MuxNodeId node_id(const MuxOutputId& output_id) const;
/* Get the node id w.r.t. the node level and node_index at the level */
MuxNodeId node_id(const size_t& node_level, const size_t& node_index_at_level) const;
/* Get the input id of a given node */
MuxInputId input_id(const MuxNodeId& node_id) const;
/* Identify if the node is an input of the MUX */
bool is_node_input(const MuxNodeId& node_id) const;
/* Get the output id of a given node */
MuxOutputId output_id(const MuxNodeId& node_id) const;
/* Identify if the node is an output of the MUX */
bool is_node_output(const MuxNodeId& node_id) const;
/* Decode memory bits based on an input id and an output id
* This function will start from the input node
* and do a forward propagation until reaching the output node
*/
vtr::vector<MuxMemId, bool> decode_memory_bits(const MuxInputId& input_id,
const MuxOutputId& output_id) const;
/* Find the input node that the memory bits will route an output node to
* This function backward propagate from the output node to an input node
* assuming the memory bits are applied
* Note: This function is mainly used for decoding LUT MUXes
*/
MuxInputId find_input_node_driven_by_output_node(const std::map<MuxMemId, bool>& memory_bits,
const MuxOutputId& output_id) const;
private: /* Private mutators : basic operations */
/* Add a unconfigured node to the MuxGraph */
MuxNodeId add_node(const enum e_mux_graph_node_type& node_type);
/* Add a edge connecting two nodes */
MuxEdgeId add_edge(const MuxNodeId& from_node, const MuxNodeId& to_node);
/* Add a memory bit to the MuxGraph */
MuxMemId add_mem();
/* Configure the level of a memory */
void set_mem_level(const MuxMemId& mem, const size_t& level);
/* Link an edge to a mem */
void set_edge_mem_id(const MuxEdgeId& edge, const MuxMemId& mem);
private: /* Private mutators : graph builders */
void build_multilevel_mux_graph(const size_t& mux_size,
const size_t& num_levels, const size_t& num_inputs_per_branch,
const CircuitModelId& pgl_model) ;
/* Build the graph for a given one-level multiplexer implementation */
void build_onelevel_mux_graph(const size_t& mux_size,
const CircuitModelId& pgl_model) ;
/* Build the graph for a given multiplexer model */
void build_mux_graph(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size);
/* Convert some internal node to outputs according to fracturable LUT circuit design specifications */
void add_fracturable_outputs(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model);
/* Build fast node lookup */
void build_node_lookup();
/* Build fast mem lookup */
void build_mem_lookup();
private: /* Private validators */
/* valid ids */
bool valid_node_id(const MuxNodeId& node) const;
bool valid_edge_id(const MuxEdgeId& edge) const;
bool valid_mem_id(const MuxMemId& mem) const;
bool valid_input_id(const MuxInputId& input_id) const;
bool valid_output_id(const MuxOutputId& output_id) const;
bool valid_level(const size_t& level) const;
/* validate/invalidate node lookup */
bool valid_node_lookup() const;
void invalidate_node_lookup();
void invalidate_mem_lookup();
/* validate graph */
bool valid_mux_graph() const;
private: /* Internal data */
vtr::vector<MuxNodeId, MuxNodeId> node_ids_; /* Unique ids for each node */
vtr::vector<MuxNodeId, enum e_mux_graph_node_type> node_types_; /* type of each node, input/output/internal */
vtr::vector<MuxNodeId, MuxInputId> node_input_ids_; /* Unique ids for each node as an input of the MUX */
vtr::vector<MuxNodeId, MuxOutputId> node_output_ids_; /* Unique ids for each node as an input of the MUX */
vtr::vector<MuxNodeId, size_t> node_levels_; /* at which level, each node belongs to */
vtr::vector<MuxNodeId, size_t> node_ids_at_level_; /* the index at the level that each node belongs to */
vtr::vector<MuxNodeId, std::vector<MuxEdgeId>> node_in_edges_; /* ids of incoming edges to each node */
vtr::vector<MuxNodeId, std::vector<MuxEdgeId>> node_out_edges_; /* ids of outgoing edges from each node */
vtr::vector<MuxEdgeId, MuxEdgeId> edge_ids_; /* Unique ids for each edge */
vtr::vector<MuxEdgeId, std::vector<MuxNodeId>> edge_src_nodes_; /* source nodes drive this edge */
vtr::vector<MuxEdgeId, std::vector<MuxNodeId>> edge_sink_nodes_; /* sink nodes this edge drives */
vtr::vector<MuxEdgeId, CircuitModelId> edge_models_; /* type of each edge: tgate/pass-gate */
vtr::vector<MuxEdgeId, MuxMemId> edge_mem_ids_; /* ids of memory bit that control the edge */
vtr::vector<MuxEdgeId, bool> edge_inv_mem_; /* if the edge is controlled by an inverted output of a memory bit */
vtr::vector<MuxMemId, MuxMemId> mem_ids_; /* ids of configuration memories */
vtr::vector<MuxMemId, size_t> mem_levels_; /* ids of configuration memories */
/* fast look-up */
typedef std::vector<std::vector<std::vector<MuxNodeId>>> NodeLookup;
mutable NodeLookup node_lookup_; /* [num_levels][num_types][num_nodes_per_level] */
typedef std::vector<std::vector<MuxMemId>> MemLookup;
mutable MemLookup mem_lookup_; /* [num_levels][num_mems_per_level] */
};
} /* End namespace openfpga*/
#endif

View File

@ -0,0 +1,33 @@
/**************************************************
* This file includes only declarations for
* the data structures to describe multiplexer structures
* Please refer to mux_graph.h for more details
*************************************************/
#ifndef MUX_GRAPH_FWD_H
#define MUX_GRAPH_FWD_H
#include "vtr_strong_id.h"
/* begin namespace openfpga */
namespace openfpga {
/* Strong Ids for MUXes */
struct mux_node_id_tag;
struct mux_edge_id_tag;
struct mux_mem_id_tag;
struct mux_input_id_tag;
struct mux_output_id_tag;
typedef vtr::StrongId<mux_node_id_tag> MuxNodeId;
typedef vtr::StrongId<mux_edge_id_tag> MuxEdgeId;
typedef vtr::StrongId<mux_mem_id_tag> MuxMemId;
typedef vtr::StrongId<mux_input_id_tag> MuxInputId;
typedef vtr::StrongId<mux_output_id_tag> MuxOutputId;
class MuxGraph;
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,121 @@
/**************************************************
* This file includes member functions for the
* data structures in mux_library.h
*************************************************/
#include "vtr_assert.h"
#include "mux_library.h"
/* begin namespace openfpga */
namespace openfpga {
/**************************************************
* Member functions for the class MuxLibrary
*************************************************/
/**************************************************
* Public accessors: aggregates
*************************************************/
MuxLibrary::mux_range MuxLibrary::muxes() const {
return vtr::make_range(mux_ids_.begin(), mux_ids_.end());
}
/**************************************************
* Public accessors: data query
*************************************************/
/* Get a MUX graph (read-only) */
MuxId MuxLibrary::mux_graph(const CircuitModelId& circuit_model,
const size_t& mux_size) const {
/* Make sure we have a valid mux look-up */
VTR_ASSERT_SAFE(valid_mux_lookup());
/* Validate circuit model id and mux_size */
VTR_ASSERT_SAFE(valid_mux_size(circuit_model, mux_size));
return mux_lookup_[circuit_model][mux_size];
}
const MuxGraph& MuxLibrary::mux_graph(const MuxId& mux_id) const {
VTR_ASSERT_SAFE(valid_mux_id(mux_id));
return mux_graphs_[mux_id];
}
/* Get a mux circuit model id */
CircuitModelId MuxLibrary::mux_circuit_model(const MuxId& mux_id) const {
VTR_ASSERT_SAFE(valid_mux_id(mux_id));
return mux_circuit_models_[mux_id];
}
/* Find the maximum mux size among the mux graphs */
size_t MuxLibrary::max_mux_size() const {
/* Iterate over all the mux graphs and find their sizes */
size_t max_mux_size = 0;
for (const auto& mux : mux_ids_) {
max_mux_size = std::max(max_mux_size, mux_graphs_[mux].num_inputs());
}
return max_mux_size;
}
/**************************************************
* Private mutators:
*************************************************/
/* Add a mux to the library */
void MuxLibrary::add_mux(const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model, const size_t& mux_size) {
/* First, check if there is already an existing graph */
if (valid_mux_size(circuit_model, mux_size)) {
return;
}
/* create a new id for the mux */
MuxId mux = MuxId(mux_ids_.size());
/* Push to the node list */
mux_ids_.push_back(mux);
/* Add a mux graph */
mux_graphs_.push_back(MuxGraph(circuit_lib, circuit_model, mux_size));
/* Recorde mux cirucit model id */
mux_circuit_models_.push_back(circuit_model);
/* update mux_lookup*/
mux_lookup_[circuit_model][mux_size] = mux;
}
/**************************************************
* Private accessors: validator and invalidators
*************************************************/
bool MuxLibrary::valid_mux_id(const MuxId& mux) const {
return size_t(mux) < mux_ids_.size() && mux_ids_[mux] == mux;
}
bool MuxLibrary::valid_mux_lookup() const {
return mux_lookup_.empty();
}
bool MuxLibrary::valid_mux_circuit_model_id(const CircuitModelId& circuit_model) const {
MuxLookup::iterator it = mux_lookup_.find(circuit_model);
return (it != mux_lookup_.end());
}
bool MuxLibrary::valid_mux_size(const CircuitModelId& circuit_model, const size_t& mux_size) const {
if (false == valid_mux_circuit_model_id(circuit_model)) {
return false;
}
std::map<size_t, MuxId>::iterator it = mux_lookup_[circuit_model].find(mux_size);
return (it != mux_lookup_[circuit_model].end());
}
/**************************************************
* Private mutators: validator and invalidators
*************************************************/
/* Build fast node lookup */
void MuxLibrary::build_mux_lookup() {
/* Invalidate the mux lookup if necessary */
invalidate_mux_lookup();
}
/* Invalidate (empty) the mux fast lookup*/
void MuxLibrary::invalidate_mux_lookup() {
mux_lookup_.clear();
}
} /* end namespace openfpga */

View File

@ -0,0 +1,64 @@
/**************************************************
* This file includes a data structure to describe
* the multiplexer implementations in FPGA architectures
* MuxLibrary is a collection of multiplexers
* with various circuit-level description (related to
* the information available in CircuitLibrary
* and the input size of multiplexers)
*************************************************/
#ifndef MUX_LIBRARY_H
#define MUX_LIBRARY_H
#include <map>
#include "mux_graph.h"
#include "mux_library_fwd.h"
/* begin namespace openfpga */
namespace openfpga {
class MuxLibrary {
public: /* Types and ranges */
typedef vtr::vector<MuxId, MuxId>::const_iterator mux_iterator;
typedef vtr::Range<mux_iterator> mux_range;
public: /* Public accessors: Aggregates */
mux_range muxes() const;
public: /* Public accessors */
/* Get a MUX graph (read-only) */
MuxId mux_graph(const CircuitModelId& circuit_model, const size_t& mux_size) const;
const MuxGraph& mux_graph(const MuxId& mux_id) const;
/* Get a mux circuit model id */
CircuitModelId mux_circuit_model(const MuxId& mux_id) const;
/* Find the mux sizes */
size_t max_mux_size() const;
public: /* Public mutators */
/* Add a mux to the library */
void add_mux(const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model, const size_t& mux_size);
public: /* Public validators */
bool valid_mux_id(const MuxId& mux) const;
private: /* Private accessors */
bool valid_mux_lookup() const;
bool valid_mux_circuit_model_id(const CircuitModelId& circuit_model) const;
bool valid_mux_size(const CircuitModelId& circuit_model, const size_t& mux_size) const;
private: /* Private mutators: mux_lookup */
void build_mux_lookup();
/* Invalidate (empty) the mux fast lookup*/
void invalidate_mux_lookup();
private: /* Internal data */
/* MUX graph-based desription */
vtr::vector<MuxId, MuxId> mux_ids_; /* Unique identifier for each mux graph */
vtr::vector<MuxId, MuxGraph> mux_graphs_; /* Graphs describing MUX internal structures */
vtr::vector<MuxId, CircuitModelId> mux_circuit_models_; /* circuit model id in circuit library */
/* Local encoder description */
//vtr::vector<MuxLocalDecoderId, Decoder> mux_local_encoders_; /* Graphs describing MUX internal structures */
/* a fast look-up to search mux_graphs with given circuit model and mux size */
typedef std::map<CircuitModelId, std::map<size_t, MuxId>> MuxLookup;
mutable MuxLookup mux_lookup_;
};
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,25 @@
/**************************************************
* This file includes only declarations for
* the data structures to describe multiplexer structures
* Please refer to mux_library.h for more details
*************************************************/
#ifndef MUX_LIBRARY_FWD_H
#define MUX_LIBRARY_FWD_H
#include "vtr_strong_id.h"
/* begin namespace openfpga */
namespace openfpga {
/* Strong Ids for MUXes */
struct mux_id_tag;
struct mux_local_decoder_id_tag;
typedef vtr::StrongId<mux_id_tag> MuxId;
typedef vtr::StrongId<mux_local_decoder_id_tag> MuxLocalDecoderId;
class MuxLibrary;
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,60 @@
/***************************************************************************************
* This file includes most utilized functions for the DecoderLibrary data structure
***************************************************************************************/
#include <cmath>
#include "vtr_assert.h"
#include "decoder_library_utils.h"
/* Begin namespace openfpga */
namespace openfpga {
/***************************************************************************************
* NOTE: This function is mainly designed for local decoders inside multiplexers
* Find the size of address lines for a decoder with a given data output size
* Addr lines
* | | ... |
* v v v
* +-----------+
* / Local \
* / Decoder \
* +-----------------+
* | | | ... | | |
* v v v v v v
* Data outputs
*
* The outputs are assumes to be one-hot codes (at most only one '1' exist)
* Considering this fact, there are only num_of_outputs + 1 conditions to be encoded.
* Therefore, the number of inputs is ceil(log(num_of_outputs+1)/log(2))
* We plus 1, which is all-zero condition for outputs
***************************************************************************************/
size_t find_mux_local_decoder_addr_size(const size_t& data_size) {
/* if data size is 1, it is an corner case for the decoder (addr = 1) */
if (1 == data_size) {
return 1;
}
VTR_ASSERT (2 <= data_size);
return ceil(log(data_size) / log(2));
}
/***************************************************************************************
* Try to find if the decoder already exists in the library,
* If there is no such decoder, add it to the library
***************************************************************************************/
DecoderId add_mux_local_decoder_to_library(DecoderLibrary& decoder_lib,
const size_t data_size) {
size_t addr_size = find_mux_local_decoder_addr_size(data_size);
DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, false, false, true);
if (DecoderId::INVALID() == decoder_id) {
/* Add the decoder */
return decoder_lib.add_decoder(addr_size, data_size, false, false, true);
}
/* There is already a decoder in the library, return the decoder id */
return decoder_id;
}
} /* End namespace openfpga*/

View File

@ -0,0 +1,21 @@
/***************************************************************************************
* Header file for most utilized functions for the DecoderLibrary data structure
***************************************************************************************/
#ifndef DECODER_LIBRARY_UTILS_H
#define DECODER_LIBRARY_UTILS_H
#include "decoder_library.h"
/* Begin namespace openfpga */
namespace openfpga {
bool need_mux_local_decoder(const size_t& data_size);
size_t find_mux_local_decoder_addr_size(const size_t& data_size);
DecoderId add_mux_local_decoder_to_library(DecoderLibrary& decoder_lib,
const size_t data_size);
} /* End namespace openfpga*/
#endif

View File

@ -0,0 +1,458 @@
/**************************************************
* This file includes a series of most utilized functions
* that are used to implement a multiplexer
*************************************************/
#include <cmath>
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_log.h"
#include "vtr_assert.h"
/* Headers from readarchopenfpga library */
#include "circuit_types.h"
#include "decoder_library_utils.h"
#include "mux_utils.h"
/* Begin namespace openfpga */
namespace openfpga {
/* Validate the number of inputs for a multiplexer implementation,
* the minimum supported size is 2
* otherwise, there is no need for a MUX
*/
bool valid_mux_implementation_num_inputs(const size_t& mux_size) {
return (2 <= mux_size);
}
/**************************************************
* Find the actual number of datapath inputs for a multiplexer implementation
* 1. if there are no requirements on constant inputs, mux_size is the actual one
* 2. if there exist constant inputs, mux_size should minus 1
* This function is mainly used to recover the number of datapath inputs
* for MUXGraphs which is a generic representation without labelling datapath inputs
*************************************************/
size_t find_mux_num_datapath_inputs(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size) {
/* Should be either MUX or LUT
* LUTs do have an tree-like MUX, but there is no need for a constant input!
*/
VTR_ASSERT ((CIRCUIT_MODEL_MUX == circuit_lib.model_type(circuit_model))
|| (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) );
if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) {
return mux_size;
}
if (true == circuit_lib.mux_add_const_input(circuit_model)) {
return mux_size - 1;
}
return mux_size;
}
/**************************************************
* Find the actual number of inputs for a multiplexer implementation
* 1. if there are no requirements on constant inputs, mux_size is the actual one
* 2. if there exist constant inputs, mux_size should plus 1
*************************************************/
size_t find_mux_implementation_num_inputs(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size) {
/* Should be either MUX or LUT
* LUTs do have an tree-like MUX, but there is no need for a constant input!
*/
VTR_ASSERT ((CIRCUIT_MODEL_MUX == circuit_lib.model_type(circuit_model))
|| (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) );
if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) {
return mux_size;
}
if (true == circuit_lib.mux_add_const_input(circuit_model)) {
return mux_size + 1;
}
return mux_size;
}
/**************************************************
* Find the structure for a multiplexer implementation
* 1. In most cases, the structure should follow the
* mux_structure defined by users in the CircuitLibrary
* 2. However, a special case may apply when mux_size is 2
* In such case, we will force a TREE structure
* regardless of users' specification as this is the
* most efficient structure
*************************************************/
enum e_circuit_model_structure find_mux_implementation_structure(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size) {
/* Ensure the mux size is valid ! */
VTR_ASSERT(valid_mux_implementation_num_inputs(mux_size));
/* Branch on the mux sizes */
if (2 == mux_size) {
/* Tree-like is the best structure of CMOS MUX2 */
if (CIRCUIT_MODEL_DESIGN_CMOS == circuit_lib.design_tech_type(circuit_model)) {
return CIRCUIT_MODEL_STRUCTURE_TREE;
}
VTR_ASSERT_SAFE(CIRCUIT_MODEL_DESIGN_RRAM == circuit_lib.design_tech_type(circuit_model));
/* One-level is the best structure of RRAM MUX2 */
return CIRCUIT_MODEL_STRUCTURE_ONELEVEL;
}
return circuit_lib.mux_structure(circuit_model);
}
/**************************************************
* Find the number of levels for a tree-like multiplexer implementation
*************************************************/
size_t find_treelike_mux_num_levels(const size_t& mux_size) {
/* Do log2(mux_size), have a basic number */
size_t level = (size_t)(log((double)mux_size)/log(2.));
/* Fix the error, i.e. mux_size=5, level = 2, we have to complete */
while (mux_size > pow(2.,(double)level)) {
level++;
}
return level;
}
/**************************************************
* Find the number of inputs for majority of branches
* in a multi-level multiplexer implementation
*************************************************/
size_t find_multilevel_mux_branch_num_inputs(const size_t& mux_size,
const size_t& mux_level) {
/* Special Case: mux_size = 2 */
if (2 == mux_size) {
return mux_size;
}
if (1 == mux_level) {
return mux_size;
}
if (2 == mux_level) {
size_t num_input_per_unit = (size_t)sqrt(mux_size);
while ( num_input_per_unit * num_input_per_unit < mux_size) {
num_input_per_unit++;
}
return num_input_per_unit;
}
VTR_ASSERT_SAFE(2 < mux_level);
size_t num_input_per_unit = 2;
while (pow((double)num_input_per_unit, (double)mux_level) < mux_size) {
num_input_per_unit++;
}
if (!valid_mux_implementation_num_inputs(num_input_per_unit)) {
VTR_LOG_ERROR("Number of inputs of each basis should be at least 2!\n");
exit(1);
}
return num_input_per_unit;
}
/**************************************************
* Build a location map for intermediate buffers
* that may appear at the multiplexing structure of a LUT
* Here is a tricky thing:
* By default, the first and last stage should not exist any intermediate buffers
* For example:
* There are 5 stages in a 4-stage multiplexer is available for buffering
* but only 3 stages [1,2,3] are intermedate buffers
* and these are users' specification
*
* +-------+ +-------+ +-------+ +-------+
* location | stage | location | stage | location | stage | location | stage | location
* [0] | [0] | [1] | [1] | [2] | [2] | [3] | [3] | [5]
* +-------+ +-------+ +-------+ +-------+
*
* We will check if the length of location map matches the number of
* multiplexer levels. And then complete a location map
* for the given multiplexers
*************************************************/
std::vector<bool> build_mux_intermediate_buffer_location_map(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& num_mux_levels) {
/* Deposite a default location map */
std::vector<bool> location_map(num_mux_levels, false);
std::string location_map_str;
/* ONLY for LUTs: intermediate buffers may exist if specified */
if (CIRCUIT_MODEL_LUT != circuit_lib.model_type(circuit_model)) {
return location_map;
}
/* Get location map when the flag of intermediate buffer is on */
if (true == circuit_lib.is_lut_intermediate_buffered(circuit_model)) {
location_map_str = circuit_lib.lut_intermediate_buffer_location_map(circuit_model);
}
/* If no location map is specified, we can return here */
if (location_map_str.empty()) {
return location_map;
}
/* Check if the user-defined location map matches the number of mux levels*/
VTR_ASSERT(num_mux_levels - 2 == location_map_str.length());
/* Apply the location_map string to the intermediate stages of multiplexers */
for (size_t i = 0; i < location_map_str.length(); ++i) {
/* '1' indicates that an intermediate buffer is needed at the location */
if ('1' == location_map_str[i]) {
location_map[i + 1] = true;
}
}
return location_map;
}
/**************************************************
* Find the number of reserved configuration bits for a multiplexer
* The reserved configuration bits is only used by ReRAM-based multiplexers
* It is actually the shared BL/WLs among ReRAMs
*************************************************/
size_t find_mux_num_reserved_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph) {
if (CIRCUIT_MODEL_DESIGN_RRAM != circuit_lib.design_tech_type(mux_model)) {
return 0;
}
std::vector<size_t> mux_branch_sizes = mux_graph.branch_sizes();
/* For tree-like multiplexers: they have two shared configuration bits */
if ( (1 == mux_branch_sizes.size())
&& (2 == mux_branch_sizes[0]) ) {
return mux_branch_sizes[0];
}
/* One-level multiplexer */
if ( 1 == mux_graph.num_levels() ) {
return mux_graph.num_inputs();
}
/* Multi-level multiplexers: TODO: This should be better tested and clarified
* Now the multi-level multiplexers are treated as cascaded one-level multiplexers
* Use the maximum branch sizes and multiply it by the number of levels
*/
std::vector<size_t>::iterator max_mux_branch_size = std::max_element(mux_branch_sizes.begin(), mux_branch_sizes.end());
return mux_graph.num_levels() * (*max_mux_branch_size);
}
/**************************************************
* Find the number of configuration bits for a CMOS multiplexer
* In general, the number of configuration bits is
* the number of memory bits for a mux_graph
* However, when local decoders are used,
* the number of configuration bits are reduced to log2(X)
*************************************************/
static
size_t find_cmos_mux_num_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph,
const e_config_protocol_type& sram_orgz_type) {
size_t num_config_bits = 0;
switch (sram_orgz_type) {
case CONFIG_MEM_MEMORY_BANK:
case CONFIG_MEM_SCAN_CHAIN:
case CONFIG_MEM_STANDALONE:
num_config_bits = mux_graph.num_memory_bits();
break;
default:
VTR_LOG_ERROR("Invalid type of SRAM organization!\n");
exit(1);
}
if (false == circuit_lib.mux_use_local_encoder(mux_model)) {
return num_config_bits;
}
num_config_bits = 0;
/* Multiplexer local encoders are applied to memory bits at each stage */
for (const auto& lvl : mux_graph.levels()) {
num_config_bits += find_mux_local_decoder_addr_size(mux_graph.num_memory_bits_at_level(lvl));
}
return num_config_bits;
}
/**************************************************
* Find the number of configuration bits for a RRAM multiplexer
* In general, the number of configuration bits is
* the number of levels for a mux_graph
* This is due to only the last BL/WL of the multiplexer is
* independent from each other
* However, when local decoders are used,
* the number of configuration bits should be consider all the
* shared(reserved) configuration bits and independent bits
*************************************************/
static
size_t find_rram_mux_num_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph,
const e_config_protocol_type& sram_orgz_type) {
size_t num_config_bits = 0;
switch (sram_orgz_type) {
case CONFIG_MEM_MEMORY_BANK:
/* In memory bank, by intensively share the Bit/Word Lines,
* we only need 1 additional BL and WL for each MUX level.
*/
num_config_bits = mux_graph.num_levels();
break;
case CONFIG_MEM_SCAN_CHAIN:
case CONFIG_MEM_STANDALONE:
/* Currently we DO NOT SUPPORT THESE, given an invalid number */
num_config_bits = size_t(-1);
break;
default:
VTR_LOG_ERROR("Invalid type of SRAM organization!\n");
exit(1);
}
if (true == circuit_lib.mux_use_local_encoder(mux_model)) {
/* TODO: this is a to-do work for ReRAM-based multiplexers and FPGAs
* The number of states of a local decoder only depends on how many
* memory bits that the multiplexer will have
* This may NOT be correct!!!
*/
return find_mux_local_decoder_addr_size(mux_graph.num_memory_bits());
}
return num_config_bits;
}
/**************************************************
* Find the number of configuration bits for
* a routing multiplexer
* Two cases are considered here.
* They are placed in different branches (sub-functions)
* in order to be easy in extending to new technology!
*************************************************/
size_t find_mux_num_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph,
const e_config_protocol_type& sram_orgz_type) {
size_t num_config_bits = size_t(-1);
switch (circuit_lib.design_tech_type(mux_model)) {
case CIRCUIT_MODEL_DESIGN_CMOS:
num_config_bits = find_cmos_mux_num_config_bits(circuit_lib, mux_model, mux_graph, sram_orgz_type);
break;
case CIRCUIT_MODEL_DESIGN_RRAM:
num_config_bits = find_rram_mux_num_config_bits(circuit_lib, mux_model, mux_graph, sram_orgz_type);
break;
default:
VTR_LOG_ERROR("Invalid design_technology of MUX(name: %s)\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
}
return num_config_bits;
}
/**************************************************
* Find the number of shared configuration bits for a CMOS multiplexer
* Currently, all the supported CMOS multiplexers
* do NOT require any shared configuration bits
*************************************************/
static
size_t find_cmos_mux_num_shared_config_bits(const e_config_protocol_type& sram_orgz_type) {
size_t num_shared_config_bits = 0;
switch (sram_orgz_type) {
case CONFIG_MEM_MEMORY_BANK:
case CONFIG_MEM_SCAN_CHAIN:
case CONFIG_MEM_STANDALONE:
num_shared_config_bits = 0;
break;
default:
VTR_LOG_ERROR("Invalid type of SRAM organization!\n");
exit(1);
}
return num_shared_config_bits;
}
/**************************************************
* Find the number of shared configuration bits for a ReRAM multiplexer
*************************************************/
static
size_t find_rram_mux_num_shared_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph,
const e_config_protocol_type& sram_orgz_type) {
size_t num_shared_config_bits = 0;
switch (sram_orgz_type) {
case CONFIG_MEM_MEMORY_BANK: {
/* In memory bank, the number of shared configuration bits is
* the sum of largest branch size at each level
*/
for (auto lvl : mux_graph.node_levels()) {
/* Find the maximum branch size:
* Note that branch_sizes() returns a sorted vector
* The last one is the maximum
*/
num_shared_config_bits += mux_graph.branch_sizes(lvl).back();
}
break;
}
case CONFIG_MEM_SCAN_CHAIN:
case CONFIG_MEM_STANDALONE:
/* Currently we DO NOT SUPPORT THESE, given an invalid number */
num_shared_config_bits = size_t(-1);
break;
default:
VTR_LOG_ERROR("Invalid type of SRAM organization!\n");
exit(1);
}
if (true == circuit_lib.mux_use_local_encoder(mux_model)) {
/* TODO: this is a to-do work for ReRAM-based multiplexers and FPGAs
* The number of states of a local decoder only depends on how many
* memory bits that the multiplexer will have
* This may NOT be correct!!!
* If local encoders are introduced, zero shared configuration bits are required
*/
return 0;
}
return num_shared_config_bits;
}
/**************************************************
* Find the number of shared configuration bits for
* a routing multiplexer
* Two cases are considered here.
* They are placed in different branches (sub-functions)
* in order to be easy in extending to new technology!
*
* Note: currently, shared configuration bits are demanded
* by ReRAM-based multiplexers only
*************************************************/
size_t find_mux_num_shared_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph,
const e_config_protocol_type& sram_orgz_type) {
size_t num_shared_config_bits = size_t(-1);
switch (circuit_lib.design_tech_type(mux_model)) {
case CIRCUIT_MODEL_DESIGN_CMOS:
num_shared_config_bits = find_cmos_mux_num_shared_config_bits(sram_orgz_type);
break;
case CIRCUIT_MODEL_DESIGN_RRAM:
num_shared_config_bits = find_rram_mux_num_shared_config_bits(circuit_lib, mux_model, mux_graph, sram_orgz_type);
break;
default:
VTR_LOG_ERROR("Invalid design_technology of MUX(name: %s)\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
}
return num_shared_config_bits;
}
} /* End namespace openfpga*/

View File

@ -0,0 +1,59 @@
#ifndef MUX_UTILS_H
#define MUX_UTILS_H
/********************************************************************
* Include header files required by the data structure definition
*******************************************************************/
#include <vector>
#include "circuit_library.h"
#include "mux_library.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* Begin namespace openfpga */
namespace openfpga {
bool valid_mux_implementation_num_inputs(const size_t& mux_size);
size_t find_mux_num_datapath_inputs(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size);
size_t find_mux_implementation_num_inputs(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size);
enum e_circuit_model_structure find_mux_implementation_structure(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& mux_size);
size_t find_treelike_mux_num_levels(const size_t& mux_size);
size_t find_multilevel_mux_branch_num_inputs(const size_t& mux_size,
const size_t& mux_level);
std::vector<bool> build_mux_intermediate_buffer_location_map(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const size_t& num_mux_levels);
size_t find_mux_num_reserved_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph);
size_t find_mux_num_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph,
const e_config_protocol_type& sram_orgz_type);
size_t find_mux_num_shared_config_bits(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph,
const e_config_protocol_type& sram_orgz_type);
} /* End namespace openfpga*/
#endif