move mux graph and decoder builders to vpr8 integration; ready to link the rr_switch to circuit models
This commit is contained in:
parent
175bef014a
commit
4367dba9b7
|
@ -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*/
|
|
@ -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
|
||||
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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*/
|
|
@ -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
|
|
@ -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*/
|
|
@ -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
|
Loading…
Reference in New Issue