234 lines
8.9 KiB
C++
234 lines
8.9 KiB
C++
/**************************************************
|
|
* This file includes a series of most utilized functions
|
|
* that are used to implement a multiplexer
|
|
*************************************************/
|
|
#include <cmath>
|
|
|
|
#include "spice_types.h"
|
|
#include "util.h"
|
|
#include "vtr_assert.h"
|
|
#include "mux_utils.h"
|
|
|
|
/* 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 ((SPICE_MODEL_MUX == circuit_lib.model_type(circuit_model))
|
|
|| (SPICE_MODEL_LUT == circuit_lib.model_type(circuit_model)) );
|
|
|
|
if (SPICE_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 ((SPICE_MODEL_MUX == circuit_lib.model_type(circuit_model))
|
|
|| (SPICE_MODEL_LUT == circuit_lib.model_type(circuit_model)) );
|
|
|
|
if (SPICE_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_spice_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 (SPICE_MODEL_DESIGN_CMOS == circuit_lib.design_tech_type(circuit_model)) {
|
|
return SPICE_MODEL_STRUCTURE_TREE;
|
|
}
|
|
VTR_ASSERT_SAFE(SPICE_MODEL_DESIGN_RRAM == circuit_lib.design_tech_type(circuit_model));
|
|
/* One-level is the best structure of RRAM MUX2 */
|
|
return SPICE_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)) {
|
|
vpr_printf(TIO_MESSAGE_ERROR,
|
|
"(File:%s,[LINE%d]) Number of inputs of each basis should be at least 2!\n",
|
|
__FILE__, __LINE__);
|
|
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 (SPICE_MODEL_LUT == circuit_lib.model_type(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;
|
|
}
|
|
|
|
/**************************************************
|
|
* Convert a linked list of MUX architecture to MuxLibrary
|
|
* TODO: this function will be deleted when MUXLibrary fully
|
|
* replace legacy data structures
|
|
*************************************************/
|
|
MuxLibrary convert_mux_arch_to_library(const CircuitLibrary& circuit_lib, t_llist* muxes_head) {
|
|
t_llist* temp = muxes_head;
|
|
MuxLibrary mux_lib;
|
|
|
|
/* Walk through the linked list */
|
|
while(temp) {
|
|
VTR_ASSERT_SAFE(NULL != temp->dptr);
|
|
t_spice_mux_model* cur_spice_mux_model = (t_spice_mux_model*)(temp->dptr);
|
|
|
|
/* Bypass the spice models who has a user-defined subckt */
|
|
if (NULL != cur_spice_mux_model->spice_model->verilog_netlist) {
|
|
/* Move on to the next*/
|
|
temp = temp->next;
|
|
continue;
|
|
}
|
|
|
|
/* Build a MUX graph for the model */
|
|
/* Find the circuit model id by the name */
|
|
CircuitModelId circuit_model = circuit_lib.model(cur_spice_mux_model->spice_model->name);
|
|
mux_lib.add_mux(circuit_lib, circuit_model, cur_spice_mux_model->size);
|
|
|
|
/* Move on to the next*/
|
|
temp = temp->next;
|
|
}
|
|
|
|
return mux_lib;
|
|
}
|