refactored grid bitstream generation

This commit is contained in:
tangxifan 2019-10-25 21:49:47 -06:00
parent fc2562fc6c
commit 3310bac65b
12 changed files with 1352 additions and 7 deletions

View File

@ -790,6 +790,24 @@ std::string generate_cb_memory_instance_name(const std::string& prefix,
return instance_name;
}
/*********************************************************************
* Generate the instance name for a configurable memory module in a
* physical block of a grid
********************************************************************/
std::string generate_pb_memory_instance_name(const std::string& prefix,
t_pb_graph_pin* pb_graph_pin,
const std::string& postfix) {
std::string instance_name(prefix);
instance_name += std::string(pb_graph_pin->parent_node->pb_type->name);
instance_name += std::string("_");
instance_name += std::string(pb_graph_pin->port->name);
instance_name += std::string("_");
instance_name += std::to_string(pb_graph_pin->pin_number);
instance_name += postfix;
return instance_name;
}
/*********************************************************************
* Generate the instance name of a grid block
**********************************************************************/

View File

@ -88,6 +88,10 @@ std::string generate_cb_memory_instance_name(const std::string& prefix,
const size_t& pin_id,
const std::string& postfix);
std::string generate_pb_memory_instance_name(const std::string& prefix,
t_pb_graph_pin* pb_graph_pin,
const std::string& postfix);
std::string generate_grid_port_name(const vtr::Point<size_t>& coordinate,
const size_t& height,
const e_side& side,

View File

@ -3205,6 +3205,25 @@ void get_mapped_lut_phy_pb_input_pin_vpack_net_num(t_phy_pb* lut_phy_pb,
return;
}
/********************************************************************
* Find the vpack_net_num of all the input pins of a LUT physical pb
*******************************************************************/
std::vector<int> find_mapped_lut_phy_pb_input_pin_vpack_net_num(t_phy_pb* lut_phy_pb) {
std::vector<int> lut_pin_net;
/* Check */
VTR_ASSERT (1 == lut_phy_pb->pb_graph_node->num_input_ports);
lut_pin_net.resize(lut_phy_pb->pb_graph_node->num_input_pins[0]);
/* Fill the array */
for (size_t ipin = 0; ipin < lut_pin_net.size(); ++ipin) {
int inode = lut_phy_pb->pb_graph_node->input_pins[0][ipin].rr_node_index_physical_pb;
lut_pin_net[ipin] = lut_phy_pb->rr_graph->rr_node[inode].vpack_net_num;
}
return lut_pin_net;
}
/* Get the vpack_net_num of all the input pins of a LUT physical pb */
void get_mapped_lut_pb_input_pin_vpack_net_num(t_pb* lut_pb,
int* num_lut_pin, int** lut_pin_net) {
@ -3225,6 +3244,25 @@ void get_mapped_lut_pb_input_pin_vpack_net_num(t_pb* lut_pb,
return;
}
/********************************************************************
* Get the vpack_net_num of all the input pins of a LUT physical pb
*******************************************************************/
std::vector<int> find_lut_logical_block_input_pin_vpack_net_num(t_logical_block* lut_logical_block) {
/* Ensure there is only one pin in the LUT logical block */
VTR_ASSERT (NULL == lut_logical_block->model->inputs[0].next);
std::vector<int> lut_pin_nets(lut_logical_block->model->inputs[0].size);
/* Fill the array */
for (size_t ipin = 0; ipin < lut_pin_nets.size(); ++ipin) {
lut_pin_nets[ipin] = lut_logical_block->input_nets[0][ipin];
}
return lut_pin_nets;
}
/* Get the vpack_net_num of all the input pins of a LUT physical pb */
void get_lut_logical_block_input_pin_vpack_net_num(t_logical_block* lut_logical_block,
int* num_lut_pin, int** lut_pin_net) {

View File

@ -247,12 +247,16 @@ void alloc_and_load_phy_pb_children_for_one_mapped_block(t_pb* cur_pb,
void get_mapped_lut_phy_pb_input_pin_vpack_net_num(t_phy_pb* lut_phy_pb,
int* num_lut_pin, int** lut_pin_net);
std::vector<int> find_mapped_lut_phy_pb_input_pin_vpack_net_num(t_phy_pb* lut_phy_pb);
void get_mapped_lut_pb_input_pin_vpack_net_num(t_pb* lut_pb,
int* num_lut_pin, int** lut_pin_net);
void get_lut_logical_block_input_pin_vpack_net_num(t_logical_block* lut_logical_block,
int* num_lut_pin, int** lut_pin_net);
std::vector<int> find_lut_logical_block_input_pin_vpack_net_num(t_logical_block* lut_logical_block);
void rec_reset_pb_type_temp_placement_index(t_pb_type* cur_pb_type);
void rec_reset_pb_type_phy_pb_type(t_pb_type* cur_pb_type);

View File

@ -7,6 +7,11 @@
#ifndef FPGA_X2P_RESERVED_WORDS_H
#define FPGA_X2P_RESERVED_WORDS_H
/* Grid naming constant strings */
constexpr char* GRID_MODULE_NAME_PREFIX = "grid_";
/* Memory naming constant strings */
constexpr char* GRID_MEM_INSTANCE_PREFIX = "mem_";
constexpr char* SWITCH_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
constexpr char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
constexpr char* MEMORY_MODULE_POSTFIX = "_mem";

View File

@ -12,6 +12,7 @@
#include "fpga_x2p_naming.h"
#include "build_grid_bitstream.h"
#include "build_routing_bitstream.h"
#include "build_device_bitstream.h"
@ -76,6 +77,9 @@ BitstreamManager build_device_bitstream(const t_vpr_setup& vpr_setup,
std::string top_block_name = generate_fpga_top_module_name();
ConfigBlockId top_block = bitstream_manager.add_block(top_block_name);
/* Create bitstream from grids */
build_grid_bitstream(bitstream_manager, top_block, module_manager, circuit_lib, mux_lib, device_size, grids);
/* Create bitstream from routing architectures */
build_routing_bitstream(bitstream_manager, top_block, module_manager, circuit_lib, mux_lib, grids, rr_switches, L_rr_node, L_device_rr_gsb);

View File

@ -0,0 +1,751 @@
/********************************************************************
* This file includes functions that are used for building bitstreams
* for grids (CLBs, heterogenerous blocks, I/Os, etc.)
*******************************************************************/
#include <string>
#include "vtr_assert.h"
#include "vtr_vector.h"
#include "util.h"
#include "mux_utils.h"
#include "vpr_types.h"
#include "globals.h"
#include "circuit_library_utils.h"
#include "fpga_x2p_reserved_words.h"
#include "fpga_x2p_types.h"
#include "fpga_x2p_naming.h"
#include "fpga_x2p_utils.h"
#include "fpga_x2p_pbtypes_utils.h"
#include "build_mux_bitstream.h"
#include "build_lut_bitstream.h"
#include "build_grid_bitstream.h"
/********************************************************************
* Decode mode bits "01..." to a bitstream vector
*******************************************************************/
static
std::vector<bool> generate_mode_select_bitstream(const std::string& mode_bits) {
std::vector<bool> mode_select_bitstream;
for (size_t i = 0; i < mode_bits.length(); ++i) {
/* Error out for unexpected bits */
if ( ('0' != mode_bits[i]) && ('1' != mode_bits[i]) ) {
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid mode_bits(%s)!\n",
__FILE__, __LINE__, mode_bits.c_str());
exit(1);
}
mode_select_bitstream.push_back('1' == mode_bits[i]);
}
return mode_select_bitstream;
}
/********************************************************************
* Generate bitstream for a primitive node and add it to bitstream manager
*******************************************************************/
static
void build_primitive_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& parent_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
t_phy_pb* primitive_pb,
t_pb_type* primitive_pb_type) {
/* Ensure a valid physical pritimive pb */
if (NULL == primitive_pb_type) {
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid primitive_pb_type!\n",
__FILE__, __LINE__);
exit(1);
}
/* Asserts */
if (NULL != primitive_pb) {
VTR_ASSERT (primitive_pb->pb_graph_node->pb_type->phy_pb_type == primitive_pb_type);
}
CircuitModelId primitive_model = primitive_pb_type->circuit_model;
VTR_ASSERT(CircuitModelId::INVALID() != primitive_model);
VTR_ASSERT( (SPICE_MODEL_IOPAD == circuit_lib.model_type(primitive_model))
|| (SPICE_MODEL_HARDLOGIC == circuit_lib.model_type(primitive_model))
|| (SPICE_MODEL_FF == circuit_lib.model_type(primitive_model)) );
/* Find SRAM ports for mode-selection */
std::vector<CircuitPortId> primitive_mode_select_ports = find_circuit_mode_select_sram_ports(circuit_lib, primitive_model);
/* We may have a port for mode select or not. */
VTR_ASSERT( (0 == primitive_mode_select_ports.size())
|| (1 == primitive_mode_select_ports.size()) );
/* Generate bitstream for mode-select ports */
if (0 == primitive_mode_select_ports.size()) {
return; /* Nothing to do, return directly */
}
std::vector<bool> mode_select_bitstream;
if (NULL != primitive_pb) {
mode_select_bitstream = generate_mode_select_bitstream(std::string(primitive_pb->mode_bits));
} else { /* get default mode_bits */
mode_select_bitstream = generate_mode_select_bitstream(std::string(primitive_pb_type->mode_bits));
}
/* Ensure the length of bitstream matches the side of memory circuits */
std::vector<CircuitModelId> sram_models = find_circuit_sram_models(circuit_lib, primitive_model);
VTR_ASSERT(1 == sram_models.size());
std::string mem_block_name = generate_memory_module_name(circuit_lib, primitive_model, sram_models[0], std::string(MEMORY_MODULE_POSTFIX));
ModuleId mem_module = module_manager.find_module(mem_block_name);
VTR_ASSERT (true == module_manager.valid_module_id(mem_module));
ModulePortId mem_out_port_id = module_manager.find_module_port(mem_module, generate_configuration_chain_data_out_name());
VTR_ASSERT(mode_select_bitstream.size() == module_manager.module_port(mem_module, mem_out_port_id).get_width());
/* Create a block for the bitstream which corresponds to the memory module associated to the LUT */
ConfigBlockId mem_block = bitstream_manager.add_block(mem_block_name);
bitstream_manager.add_child_block(parent_configurable_block, mem_block);
/* Add the bitstream to the bitstream manager */
for (const bool& bit : mode_select_bitstream) {
ConfigBitId config_bit = bitstream_manager.add_bit(bit);
/* Link the memory bits to the mux mem block */
bitstream_manager.add_bit_to_block(mem_block, config_bit);
}
}
/********************************************************************
* Generate bitstream for a LUT and add it to bitstream manager
* This function supports both single-output and fracturable LUTs
*******************************************************************/
static
void build_lut_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& parent_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
t_phy_pb* lut_pb,
t_pb_type* lut_pb_type) {
/* Ensure a valid physical pritimive pb */
if (NULL == lut_pb_type) {
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid lut_pb_type!\n",
__FILE__, __LINE__);
exit(1);
}
/* Asserts */
if (NULL != lut_pb) {
VTR_ASSERT (lut_pb->pb_graph_node->pb_type->phy_pb_type == lut_pb_type);
}
CircuitModelId lut_model = lut_pb_type->circuit_model;
VTR_ASSERT (CircuitModelId::INVALID() != lut_model);
VTR_ASSERT (SPICE_MODEL_LUT == circuit_lib.model_type(lut_model));
/* Find the input ports for LUT size, this is used to decode the LUT memory bits! */
std::vector<CircuitPortId> model_input_ports = circuit_lib.model_ports_by_type(lut_model, SPICE_MODEL_PORT_INPUT, true);
VTR_ASSERT(1 == model_input_ports.size());
size_t lut_size = circuit_lib.port_size(model_input_ports[0]);
/* Find SRAM ports for truth tables and mode-selection */
std::vector<CircuitPortId> lut_regular_sram_ports = find_circuit_regular_sram_ports(circuit_lib, lut_model);
std::vector<CircuitPortId> lut_mode_select_ports = find_circuit_mode_select_sram_ports(circuit_lib, lut_model);
/* We should always 1 regular sram port, where truth table is loaded to */
VTR_ASSERT(1 == lut_regular_sram_ports.size());
/* We may have a port for mode select or not. This depends on if the LUT is fracturable or not */
VTR_ASSERT( (0 == lut_mode_select_ports.size())
|| (1 == lut_mode_select_ports.size()) );
std::vector<bool> lut_bitstream;
/* Generate bitstream for the LUT */
if ( (NULL == lut_pb)
|| ((NULL != lut_pb && 0 == lut_pb->num_logical_blocks)) ) {
/* An empty pb means that this is an unused LUT,
* we give an empty truth table, which are full of default values (defined by users)
*/
for (size_t i = 0; i < circuit_lib.port_size(lut_regular_sram_ports[0]); ++i) {
VTR_ASSERT( (0 == circuit_lib.port_default_value(lut_regular_sram_ports[0]))
|| (1 == circuit_lib.port_default_value(lut_regular_sram_ports[0])) );
lut_bitstream.push_back(1 == circuit_lib.port_default_value(lut_regular_sram_ports[0]));
}
} else {
VTR_ASSERT (NULL != lut_pb);
/* Pre-allocate truth tables for a LUT,
* Note: for fracturable LUTs, there could be several truth tables
* since multiple functions are mapped to the same LUT but to different outputs
*/
std::vector<LutTruthTable> truth_tables;
truth_tables.resize(lut_pb->num_logical_blocks);
/* Find truth tables and decode them one by one
* Fracturable LUT may have multiple truth tables,
* which should be grouped in a unique one
* And then we derive the truth table
*/
for (int i = 0; i < lut_pb->num_logical_blocks; ++i) {
int mapped_logical_block_index = lut_pb->logical_block[i];
/* For wired LUT we provide a default truth table */
if (TRUE == lut_pb->is_wired_lut[i]) {
/* Build a post-routing lut truth table */
std::vector<int> lut_pin_nets = find_mapped_lut_phy_pb_input_pin_vpack_net_num(lut_pb);
truth_tables[i] = build_post_routing_wired_lut_truth_table(lut_pb->rr_graph->rr_node[lut_pb->lut_output_pb_graph_pin[i]->rr_node_index_physical_pb].vpack_net_num, lut_pin_nets.size(), lut_pin_nets);
} else {
/* For regular LUTs, we generate the truth tables */
VTR_ASSERT (FALSE == lut_pb->is_wired_lut[i]);
VTR_ASSERT (VPACK_COMB == logical_block[mapped_logical_block_index].type);
/* Get the mapped vpack_net_num of this physical LUT pb */
std::vector<int> lut_pin_nets = find_mapped_lut_phy_pb_input_pin_vpack_net_num(lut_pb);
/* Consider LUT pin remapping when assign lut truth tables */
/* Match truth table and post-routing results */
truth_tables[i] = build_post_routing_lut_truth_table(&logical_block[mapped_logical_block_index],
lut_pin_nets.size(), lut_pin_nets);
}
/* Adapt truth table for a fracturable LUT
* TODO: Determine fixed input bits for this truth table:
* 1. input bits within frac_level (all '-' if not specified)
* 2. input bits outside frac_level, decoded to its output mask (0 -> first part -> all '1')
*/
truth_tables[i] = adapt_truth_table_for_frac_lut(circuit_lib, lut_pb->lut_output_pb_graph_pin[i],
truth_tables[i]);
}
/* Find MUX graph correlated to the LUT */
MuxId lut_mux_id = mux_lib.mux_graph(lut_model, (size_t)pow(2., lut_size));
const MuxGraph& mux_graph = mux_lib.mux_graph(lut_mux_id);
/* Ensure the LUT MUX has the expected input and SRAM port sizes */
VTR_ASSERT(mux_graph.num_memory_bits() == lut_size);
VTR_ASSERT(mux_graph.num_inputs() == (size_t)pow(2., lut_size));
/* Generate LUT bitstream */
lut_bitstream = build_frac_lut_bitstream(circuit_lib, mux_graph,
lut_pb, truth_tables,
circuit_lib.port_default_value(lut_regular_sram_ports[0]));
}
/* Generate bitstream for mode-select ports */
if (0 != lut_mode_select_ports.size()) {
std::vector<bool> mode_select_bitstream;
if (NULL != lut_pb) {
mode_select_bitstream = generate_mode_select_bitstream(std::string(lut_pb->mode_bits));
} else { /* get default mode_bits */
mode_select_bitstream = generate_mode_select_bitstream(std::string(lut_pb_type->mode_bits));
}
/* Conjunct the mode-select bitstream to the lut bitstream */
for (const bool& bit : mode_select_bitstream) {
lut_bitstream.push_back(bit);
}
}
/* Ensure the length of bitstream matches the side of memory circuits */
std::vector<CircuitModelId> sram_models = find_circuit_sram_models(circuit_lib, lut_model);
VTR_ASSERT(1 == sram_models.size());
std::string mem_block_name = generate_memory_module_name(circuit_lib, lut_model, sram_models[0], std::string(MEMORY_MODULE_POSTFIX));
ModuleId mem_module = module_manager.find_module(mem_block_name);
VTR_ASSERT (true == module_manager.valid_module_id(mem_module));
ModulePortId mem_out_port_id = module_manager.find_module_port(mem_module, generate_configuration_chain_data_out_name());
VTR_ASSERT(lut_bitstream.size() == module_manager.module_port(mem_module, mem_out_port_id).get_width());
/* Create a block for the bitstream which corresponds to the memory module associated to the LUT */
ConfigBlockId mem_block = bitstream_manager.add_block(mem_block_name);
bitstream_manager.add_child_block(parent_configurable_block, mem_block);
/* Add the bitstream to the bitstream manager */
for (const bool& bit : lut_bitstream) {
ConfigBitId config_bit = bitstream_manager.add_bit(bit);
/* Link the memory bits to the mux mem block */
bitstream_manager.add_bit_to_block(mem_block, config_bit);
}
}
/********************************************************************
* This function generates bitstream for a programmable routing
* multiplexer which drives an output pin of physical_pb_graph_node and its the input_edges
*
* src_pb_graph_node.[in|out]_pins -----------------> des_pb_graph_node.[in|out]pins
* /|\
* |
* input_pins, edges, output_pins
*******************************************************************/
static
void build_physical_block_pin_interc_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& parent_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
t_pb_graph_pin* des_pb_graph_pin,
t_mode* physical_mode,
const int& path_id) {
/* 1. identify pin interconnection type,
* 2. Identify the number of fan-in (Consider interconnection edges of only selected mode)
* 3. Select and print the SPICE netlist
*/
int fan_in = 0;
t_interconnect* cur_interc = NULL;
find_interc_fan_in_des_pb_graph_pin(des_pb_graph_pin, physical_mode, &cur_interc, &fan_in);
if ((NULL == cur_interc) || (0 == fan_in)) {
/* No interconnection matched */
return;
}
enum e_interconnect interc_type = determine_actual_pb_interc_type(cur_interc, fan_in);
switch (interc_type) {
case DIRECT_INTERC:
/* Nothing to do, return */
break;
case COMPLETE_INTERC:
case MUX_INTERC: {
/* Find the circuit model id of the mux, we need its design technology which matters the bitstream generation */
CircuitModelId mux_model = cur_interc->circuit_model;
VTR_ASSERT(SPICE_MODEL_MUX == circuit_lib.model_type(mux_model));
/* Find the input size of the implementation of a routing multiplexer */
size_t datapath_mux_size = fan_in;
VTR_ASSERT(true == valid_mux_implementation_num_inputs(datapath_mux_size));
/* Generate bitstream depend on both technology and structure of this MUX */
std::vector<bool> mux_bitstream = build_mux_bitstream(circuit_lib, mux_model, mux_lib, datapath_mux_size, path_id);
/* Create the block denoting the memory instances that drives this node in physical_block */
std::string mem_block_name = generate_pb_memory_instance_name(GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""));
ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name);
bitstream_manager.add_child_block(parent_configurable_block, mux_mem_block);
/* Find the module in module manager and ensure the bitstream size matches! */
std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX));
ModuleId mux_mem_module = module_manager.find_module(mem_module_name);
VTR_ASSERT (true == module_manager.valid_module_id(mux_mem_module));
ModulePortId mux_mem_out_port_id = module_manager.find_module_port(mux_mem_module, generate_configuration_chain_data_out_name());
VTR_ASSERT(mux_bitstream.size() == module_manager.module_port(mux_mem_module, mux_mem_out_port_id).get_width());
/* Add the bistream to the bitstream manager */
for (const bool& bit : mux_bitstream) {
ConfigBitId config_bit = bitstream_manager.add_bit(bit);
/* Link the memory bits to the mux mem block */
bitstream_manager.add_bit_to_block(mux_mem_block, config_bit);
}
break;
}
default:
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s,[LINE%d])Invalid interconnection type for %s (Arch[LINE%d])!\n",
__FILE__, __LINE__, cur_interc->name, cur_interc->line_num);
exit(1);
}
}
/********************************************************************
* This function generates bitstream for the programmable routing
* multiplexers in a pb_graph node
*******************************************************************/
static
void build_physical_block_interc_port_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& parent_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
t_pb_graph_node* physical_pb_graph_node,
t_phy_pb* physical_pb,
const e_spice_pb_port_type& pb_port_type,
t_mode* physical_mode) {
switch (pb_port_type) {
case SPICE_PB_PORT_INPUT:
for (int iport = 0; iport < physical_pb_graph_node->num_input_ports; ++iport) {
for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) {
/* If this is a idle block, we set 0 to the selected edge*/
/* Get the selected edge of current pin*/
int path_id;
if (NULL == physical_pb) {
path_id = DEFAULT_PATH_ID;
} else {
VTR_ASSERT(NULL != physical_pb);
t_rr_node* pb_rr_nodes = physical_pb->rr_graph->rr_node;
int node_index = physical_pb_graph_node->input_pins[iport][ipin].rr_node_index_physical_pb;
int prev_node = pb_rr_nodes[node_index].prev_node;
/* prev_edge = pb_rr_nodes[node_index].prev_edge; */
/* Make sure this pb_rr_node is not OPEN and is not a primitive output*/
if (OPEN == prev_node) {
path_id = DEFAULT_PATH_ID;
} else {
/* Find the path_id */
path_id = find_path_id_between_pb_rr_nodes(pb_rr_nodes, prev_node, node_index);
VTR_ASSERT(DEFAULT_PATH_ID != path_id);
}
/* TODO: This should be done outside this function!
* Path id for the sdc generation
*/
pb_rr_nodes[node_index].id_path = path_id;
}
build_physical_block_pin_interc_bitstream(bitstream_manager, parent_configurable_block,
module_manager, circuit_lib, mux_lib,
&(physical_pb_graph_node->input_pins[iport][ipin]),
physical_mode,
path_id);
}
}
break;
case SPICE_PB_PORT_OUTPUT:
for (int iport = 0; iport < physical_pb_graph_node->num_output_ports; ++iport) {
for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) {
/* If this is a idle block, we set 0 to the selected edge*/
/* Get the selected edge of current pin*/
int path_id;
if (NULL == physical_pb) {
path_id = DEFAULT_PATH_ID;
} else {
VTR_ASSERT(NULL != physical_pb);
t_rr_node* pb_rr_nodes = physical_pb->rr_graph->rr_node;
int node_index = physical_pb_graph_node->output_pins[iport][ipin].rr_node_index_physical_pb;
int prev_node = pb_rr_nodes[node_index].prev_node;
/* prev_edge = pb_rr_nodes[node_index].prev_edge; */
/* Make sure this pb_rr_node is not OPEN and is not a primitive output*/
if (OPEN == prev_node) {
path_id = DEFAULT_PATH_ID;
} else {
/* Find the path_id */
path_id = find_path_id_between_pb_rr_nodes(pb_rr_nodes, prev_node, node_index);
VTR_ASSERT(DEFAULT_PATH_ID != path_id);
}
/* TODO: This should be done outside this function!
* Path id for the sdc generation
*/
pb_rr_nodes[node_index].id_path = path_id;
}
build_physical_block_pin_interc_bitstream(bitstream_manager, parent_configurable_block,
module_manager, circuit_lib, mux_lib,
&(physical_pb_graph_node->output_pins[iport][ipin]),
physical_mode,
path_id);
}
}
break;
case SPICE_PB_PORT_CLOCK:
for (int iport = 0; iport < physical_pb_graph_node->num_clock_ports; ++iport) {
for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) {
/* If this is a idle block, we set 0 to the selected edge*/
/* Get the selected edge of current pin*/
int path_id;
if (NULL == physical_pb) {
path_id = DEFAULT_PATH_ID;
} else {
VTR_ASSERT(NULL != physical_pb);
t_rr_node* pb_rr_nodes = physical_pb->rr_graph->rr_node;
int node_index = physical_pb_graph_node->clock_pins[iport][ipin].rr_node_index_physical_pb;
int prev_node = pb_rr_nodes[node_index].prev_node;
/* prev_edge = pb_rr_nodes[node_index].prev_edge; */
/* Make sure this pb_rr_node is not OPEN and is not a primitive output*/
if (OPEN == prev_node) {
path_id = DEFAULT_PATH_ID;
} else {
/* Find the path_id */
path_id = find_path_id_between_pb_rr_nodes(pb_rr_nodes, prev_node, node_index);
VTR_ASSERT(DEFAULT_PATH_ID != path_id);
}
/* TODO: This should be done outside this function!
* Path id for the sdc generation
*/
pb_rr_nodes[node_index].id_path = path_id;
}
build_physical_block_pin_interc_bitstream(bitstream_manager, parent_configurable_block,
module_manager, circuit_lib, mux_lib,
&(physical_pb_graph_node->clock_pins[iport][ipin]),
physical_mode,
path_id);
}
}
break;
default:
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid pb port type!\n",
__FILE__, __LINE__);
exit(1);
}
}
/********************************************************************
* This function generates bitstream for the programmable routing
* multiplexers in a pb_graph node
*******************************************************************/
static
void build_physical_block_interc_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& parent_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
t_pb_graph_node* physical_pb_graph_node,
t_phy_pb* physical_pb,
const int& physical_mode_index) {
/* Check if the pb_graph node is valid or not */
if (NULL == physical_pb_graph_node) {
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid physical_pb_graph_node.\n",
__FILE__, __LINE__);
exit(1);
}
/* Assign current mode */
t_mode* physical_mode = &(physical_pb_graph_node->pb_type->modes[physical_mode_index]);
/* We check output_pins of physical_pb_graph_node and its the input_edges
* Iterate over the interconnections between outputs of physical_pb_graph_node
* and outputs of child_pb_graph_node
* child_pb_graph_node.output_pins -----------------> physical_pb_graph_node.outpins
* /|\
* |
* input_pins, edges, output_pins
*/
build_physical_block_interc_port_bitstream(bitstream_manager, parent_configurable_block,
module_manager, circuit_lib, mux_lib,
physical_pb_graph_node, physical_pb,
SPICE_PB_PORT_OUTPUT, physical_mode);
/* We check input_pins of child_pb_graph_node and its the input_edges
* Iterate over the interconnections between inputs of physical_pb_graph_node
* and inputs of child_pb_graph_node
* physical_pb_graph_node.input_pins -----------------> child_pb_graph_node.input_pins
* /|\
* |
* input_pins, edges, output_pins
*/
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ipb++) {
for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; jpb++) {
t_pb_graph_node* child_pb_graph_node = &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ipb][jpb]);
/* branch on empty pb */
t_phy_pb* child_pb = NULL;
if (NULL != physical_pb) {
child_pb = &(physical_pb->child_pbs[ipb][jpb]);
}
/* For each child_pb_graph_node input pins*/
build_physical_block_interc_port_bitstream(bitstream_manager, parent_configurable_block,
module_manager, circuit_lib, mux_lib,
child_pb_graph_node, child_pb,
SPICE_PB_PORT_INPUT, physical_mode);
/* For clock pins, we should do the same work */
build_physical_block_interc_port_bitstream(bitstream_manager, parent_configurable_block,
module_manager, circuit_lib, mux_lib,
child_pb_graph_node, child_pb,
SPICE_PB_PORT_CLOCK, physical_mode);
}
}
}
/********************************************************************
* This function generates bitstream for a physical block, which is
* a child block of a grid
* This function will follow a recursive way in generating bitstreams
* It will follow the same sequence in visiting all the sub blocks
* in a physical as we did during module generation
*
* Note: if you want to bind your bitstream with a FPGA fabric generated by FPGA-X2P
* Please follow the same sequence in visiting pb_graph nodes!!!
* For more details, you may refer to function rec_build_physical_block_modules()
*******************************************************************/
static
void rec_build_physical_block_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& parent_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const e_side& border_side,
t_phy_pb* physical_pb,
t_pb_graph_node* physical_pb_graph_node) {
/* Get the physical pb_type that is linked to the pb_graph node */
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
/* Find the mode that define_idle_mode*/
int physical_mode_index = find_pb_type_physical_mode_index((*physical_pb_type));
/* Create a block for the physical block under the grid block in bitstream manager */
std::string pb_block_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side);
std::string pb_block_name = generate_physical_block_module_name(pb_block_name_prefix, physical_pb_type);
ConfigBlockId pb_configurable_block = bitstream_manager.add_block(pb_block_name);
bitstream_manager.add_child_block(parent_configurable_block, pb_configurable_block);
/* Recursively finish all the child pb_types*/
if (false == is_primitive_pb_type(physical_pb_type)) {
for (int ipb = 0; ipb < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ipb) {
for (int jpb = 0; jpb < physical_pb_type->modes[physical_mode_index].pb_type_children[ipb].num_pb; ++jpb) {
t_phy_pb* child_pb = NULL;
/* Find the child pb that is mapped, and the mapping info is not stored in the physical mode ! */
if (NULL != physical_pb) {
child_pb = get_phy_child_pb_for_phy_pb_graph_node(physical_pb, ipb, jpb);
}
/* Go recursively */
rec_build_physical_block_bitstream(bitstream_manager, pb_configurable_block,
module_manager, circuit_lib, mux_lib,
border_side,
child_pb,
&(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ipb][jpb]));
}
}
}
/* Check if this has defined a spice_model*/
if (true == is_primitive_pb_type(physical_pb_type)) {
switch (physical_pb_type->class_type) {
case LUT_CLASS:
/* Special case for LUT !!!
* Mapped logical block information is stored in child_pbs of this pb!!!
*/
build_lut_bitstream(bitstream_manager, pb_configurable_block,
module_manager, circuit_lib, mux_lib,
physical_pb, physical_pb_type);
break;
case LATCH_CLASS:
case UNKNOWN_CLASS:
case MEMORY_CLASS:
/* For other types of blocks, we can apply a generic therapy */
build_primitive_bitstream(bitstream_manager, pb_configurable_block,
module_manager, circuit_lib,
physical_pb, physical_pb_type);
break;
default:
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Unknown class type of pb_type(%s)!\n",
__FILE__, __LINE__, physical_pb_type->name);
exit(1);
}
/* Finish for primitive node, return */
return;
}
/* Generate the bitstream for the interconnection in this physical block */
build_physical_block_interc_bitstream(bitstream_manager, pb_configurable_block,
module_manager, circuit_lib, mux_lib,
physical_pb->pb_graph_node, physical_pb, physical_mode_index);
}
/********************************************************************
* This function generates bitstream for a grid, which could be a
* CLB, a heterogenerous block, an I/O, etc.
* Note that each grid may contain a number of physical blocks,
* this function will iterate over them
*******************************************************************/
static
void build_physical_block_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& top_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const std::vector<std::vector<t_grid_tile>>& grids,
const vtr::Point<size_t>& grid_coordinate,
const e_side& border_side) {
/* Create a block for the grid in bitstream manager */
t_type_ptr grid_type = grids[grid_coordinate.x()][grid_coordinate.y()].type;
std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX);
std::string grid_block_name = generate_grid_block_instance_name(grid_module_name_prefix, std::string(grid_type->name),
IO_TYPE == grid_type, border_side, grid_coordinate);
ConfigBlockId grid_configurable_block = bitstream_manager.add_block(grid_block_name);
bitstream_manager.add_child_block(top_block, grid_configurable_block);
/* Iterate over the capacity of the grid */
for (int z = 0; z < grids[grid_coordinate.x()][grid_coordinate.y()].type->capacity; ++z) {
/* Get the top-level node of the pb_graph */
t_pb_graph_node* top_pb_graph_node = grid_type->pb_graph_head;
VTR_ASSERT(NULL != top_pb_graph_node);
/* Check in all the mapped blocks(clustered logic block), there is a match x,y,z*/
t_block* mapped_block = search_mapped_block(grid_coordinate.x(), grid_coordinate.y(), z);
t_phy_pb* top_pb;
if (NULL != mapped_block) {
top_pb = (t_phy_pb*)mapped_block->phy_pb;
VTR_ASSERT(NULL != top_pb);
}
/* Recursively traverse the pb_graph and generate bitstream */
rec_build_physical_block_bitstream(bitstream_manager, grid_configurable_block,
module_manager, circuit_lib, mux_lib,
border_side,
top_pb, top_pb_graph_node);
}
}
/********************************************************************
* Top-level function of this file:
* Generate bitstreams for all the grids, including
* 1. core grids that sit in the center of the fabric
* 2. side grids (I/O grids) that sit in the borders for the fabric
*******************************************************************/
void build_grid_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& top_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const vtr::Point<size_t>& device_size,
const std::vector<std::vector<t_grid_tile>>& grids) {
vpr_printf(TIO_MESSAGE_INFO,
"Generating bitstream for core grids...\n");
/* Generate bitstream for the core logic block one by one */
for (size_t ix = 1; ix < device_size.x() - 1; ++ix) {
for (size_t iy = 1; iy < device_size.y() - 1; ++iy) {
/* Bypass EMPTY grid */
if (EMPTY_TYPE == grids[ix][iy].type) {
continue;
}
/* Skip height > 1 tiles (mostly heterogeneous blocks) */
if (0 < grids[ix][iy].offset) {
continue;
}
/* We should not meet any I/O grid */
VTR_ASSERT(IO_TYPE != grids[ix][iy].type);
/* Ensure a valid usage */
VTR_ASSERT((0 == grids[ix][iy].usage)||(0 < grids[ix][iy].usage));
/* Add a grid module to top_module*/
vtr::Point<size_t> grid_coord(ix, iy);
build_physical_block_bitstream(bitstream_manager, top_block, module_manager,
circuit_lib, mux_lib, grids, grid_coord, NUM_SIDES);
}
}
vpr_printf(TIO_MESSAGE_INFO,
"Generating bitstream for I/O grids...\n");
/* Create the coordinate range for each side of FPGA fabric */
std::vector<e_side> io_sides{TOP, RIGHT, BOTTOM, LEFT};
std::map<e_side, std::vector<vtr::Point<size_t>>> io_coordinates;
/* TOP side*/
for (size_t ix = 1; ix < device_size.x() - 1; ++ix) {
io_coordinates[TOP].push_back(vtr::Point<size_t>(ix, device_size.y() - 1));
}
/* RIGHT side */
for (size_t iy = 1; iy < device_size.y() - 1; ++iy) {
io_coordinates[RIGHT].push_back(vtr::Point<size_t>(device_size.x() - 1, iy));
}
/* BOTTOM side*/
for (size_t ix = 1; ix < device_size.x() - 1; ++ix) {
io_coordinates[BOTTOM].push_back(vtr::Point<size_t>(ix, 0));
}
/* LEFT side */
for (size_t iy = 1; iy < device_size.y() - 1; ++iy) {
io_coordinates[LEFT].push_back(vtr::Point<size_t>(0, iy));
}
/* Add instances of I/O grids to top_module */
for (const e_side& io_side : io_sides) {
for (const vtr::Point<size_t>& io_coordinate : io_coordinates[io_side]) {
/* Bypass EMPTY grid */
if (EMPTY_TYPE == grids[io_coordinate.x()][io_coordinate.y()].type) {
continue;
}
/* Skip height > 1 tiles (mostly heterogeneous blocks) */
if (0 < grids[io_coordinate.x()][io_coordinate.y()].offset) {
continue;
}
/* We should not meet any I/O grid */
VTR_ASSERT(IO_TYPE == grids[io_coordinate.x()][io_coordinate.y()].type);
build_physical_block_bitstream(bitstream_manager, top_block, module_manager,
circuit_lib, mux_lib, grids, io_coordinate, io_side);
}
}
}

View File

@ -0,0 +1,22 @@
/********************************************************************
* Header file for build_grid_bitstream.cpp
*******************************************************************/
#ifndef BUILD_GRID_BITSTREAM_H
#define BUILD_GRID_BITSTREAM_H
#include <vector>
#include "vtr_geometry.h"
#include "vpr_types.h"
#include "bitstream_manager.h"
#include "module_manager.h"
#include "circuit_library.h"
#include "mux_library.h"
void build_grid_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& top_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const vtr::Point<size_t>& device_size,
const std::vector<std::vector<t_grid_tile>>& grids);
#endif

View File

@ -0,0 +1,457 @@
/*********************************************************************
* This file includes functions that are used for building bitstreams
* for Look-Up Tables
********************************************************************/
#include <cmath>
#include <cstring>
#include "vtr_assert.h"
#include "util.h"
#include "vpr_types.h"
#include "string_token.h"
#include "fpga_x2p_utils.h"
#include "fpga_x2p_pbtypes_utils.h"
#include "build_lut_bitstream.h"
/********************************************************************
* Adapt the truth table from the short-wire connection
* from the input nets of a LUT to an output of a LUT
*
* LUT
* +-------------+
* lut_input--->|----+ |
* | | |
* | +------->|---> lut_output
* | |
* +-------------+
*
* In this case, LUT is configured as a wiring module
* This function will generate a truth for the wiring LUT
********************************************************************/
LutTruthTable build_post_routing_wired_lut_truth_table(const int& lut_output_vpack_net_num,
const size_t& lut_size,
const std::vector<int>& lut_pin_vpack_net_num) {
LutTruthTable tt;
/* There is always only one line in this truth table */
tt.resize(1);
/* Pre-allocate the truth table:
* Each truth table line is organized in BLIF format:
* |<---LUT size--->|
* < a string of 0 or 1> <0 or 1>
* The first <lut_size> of characters represent the input values of each LUT input
* Here, we add 2 characters, which denote the space and a digit (0|1)
* By default, we set all the inputs as don't care value '-'
*
* For more details, please refer to the BLIF format documentation
*/
tt[0].resize(lut_size, '-');
/* Fill the truth table !!! */
for (size_t inet = 0; inet < lut_size; ++inet) {
/* Find the vpack_num in the lut_input_pin, we fix it to be 1 */
if (lut_output_vpack_net_num == lut_pin_vpack_net_num[inet]) {
tt[0][inet] = '1';
}
}
tt[0] += std::string(" 1");
return tt;
}
/********************************************************************
* Provide the truth table of a mapped logical block
* 1. Reorgainze the truth table to be consistent with the mapped nets of a LUT
* 2. Allocate the truth table in a clean string and return
********************************************************************/
LutTruthTable build_post_routing_lut_truth_table(t_logical_block* mapped_logical_block,
const size_t& lut_size,
const std::vector<int>& lut_pin_vpack_net_num) {
if (NULL == mapped_logical_block) {
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid mapped_logical_block!\n",
__FILE__, __LINE__);
exit(1);
}
/* Create a map between the lut net ids and logical block net ids */
std::vector<int> lut_to_lb_net_mapping(lut_size, OPEN);
/* Find nets mapped to a logical block */
std::vector<int> lb_pin_vpack_net_num = find_lut_logical_block_input_pin_vpack_net_num(mapped_logical_block);
/* Create a pin-to-pin net_num mapping */
for (size_t inet = 0; inet < lut_size; ++inet) {
/* Bypass open nets */
if (OPEN == lut_pin_vpack_net_num[inet]) {
continue;
}
VTR_ASSERT_SAFE (OPEN != lut_pin_vpack_net_num[inet]);
/* Find the position (offset) of each vpack_net_num in lb_pins */
for (size_t jnet = 0; jnet < lb_pin_vpack_net_num.size(); ++jnet) {
if (lut_pin_vpack_net_num[inet] == lb_pin_vpack_net_num[jnet]) {
lut_to_lb_net_mapping[inet] = jnet;
break;
}
}
/* Not neccesary to find a one, some luts just share part of their pins */
}
/* Count the lines of truth table stored in the mapped logical block */
struct s_linked_vptr* head = mapped_logical_block->truth_table;
/* Convert the truth_tables stored in the mapped logical block */
LutTruthTable truth_table;
/* Handle the truth table pin remapping
* Note that we cannot simply copy the original truth table from the mapped logical block
* Due to the logic equivalence of LUT pins, the nets are not longer in the sequences
* that are defined in the original truth table
* An illustrative example:
*
* Original Truth Table Post VPR Truth Table
*
* +-------+ +-------+
* net0 --->| | net1--->| |
* net1 --->| LUT | net0--->| LUT |
* ... | | ... | |
* +-------+ +-------+
*/
while (head) {
/* Cache a line of truth table */
std::string tt_line;
/* Reorganize the original truth table */
for (size_t inet = 0; inet < lut_size; ++inet) {
/* Open net implies a don't care, or some nets are not in the list */
if ( (OPEN == lut_pin_vpack_net_num[inet])
|| (OPEN == lut_to_lb_net_mapping[inet])) {
tt_line.push_back('-');
continue;
}
/* Find the desired truth table bit */
tt_line.push_back(((char*)(head->data_vptr))[lut_to_lb_net_mapping[inet]]);
}
/* Copy the last two characters from original truth table, which is not changed even after VPR implementation */
int lb_truth_table_size = strlen((char*)(head->data_vptr));
tt_line += std::string((char*)(head->data_vptr) + lb_truth_table_size - 2, 2);
/* Add the line to truth table */
truth_table.push_back(tt_line);
/* Go to next line */
head = head->next;
}
return truth_table;
}
/********************************************************************
* Adapt truth table for a fracturable LUT
* Determine fixed input bits for this truth table:
* 1. input bits within frac_level (all '-' if not specified)
* 2. input bits outside frac_level, decoded to its output mask (0 -> first part -> all '1')
********************************************************************/
LutTruthTable adapt_truth_table_for_frac_lut(const CircuitLibrary& circuit_lib,
t_pb_graph_pin* lut_out_pb_graph_pin,
const LutTruthTable& truth_table) {
LutTruthTable adapt_truth_table;
/* Find the output port of LUT that this logical block is mapped to */
VTR_ASSERT(NULL != lut_out_pb_graph_pin);
/* find the corresponding SPICE model output port and assoicated lut_output_mask */
CircuitPortId lut_model_output_port = lut_out_pb_graph_pin->port->circuit_model_port;
size_t lut_frac_level = circuit_lib.port_lut_frac_level(lut_model_output_port);
/* No adaption required for when the lut_frac_level is not set */
if (size_t(OPEN) == lut_frac_level) {
return truth_table;
}
/* Find the corresponding circuit model output port and assoicated lut_output_mask */
size_t lut_output_mask = circuit_lib.port_lut_output_masks(lut_model_output_port)[lut_out_pb_graph_pin->pin_number];
/* Apply modification to the truth table */
for (const std::string& tt_line : truth_table) {
/* Last two chars are fixed */
size_t lut_size = tt_line.length() - 2;
/* Get the number of bits to be masked (modified) */
int num_mask_bits = lut_size - lut_frac_level;
/* Check if we need to modify any bits */
VTR_ASSERT(0 <= num_mask_bits);
if ( 0 == num_mask_bits ) {
continue;
}
/* Modify bits starting from lut_frac_level */
/* Decode the lut_output_mask to LUT input codes */
int temp = pow(2., num_mask_bits) - 1 - lut_output_mask;
std::vector<size_t> mask_bits = my_itobin_vec(temp, num_mask_bits);
/* Copy the bits to the truth table line */
std::string adapt_tt_line = tt_line;
for (const size_t& mask_bit : mask_bits) {
adapt_tt_line.insert(lut_frac_level, std::to_string(mask_bit));
}
/* Push to adapted truth table */
adapt_truth_table.push_back(adapt_tt_line);
}
return adapt_truth_table;
}
/********************************************************************
* Determine if the truth table of a LUT is a on-set or a off-set
*******************************************************************/
static
bool lut_truth_table_use_on_set(const LutTruthTable& truth_table) {
bool on_set = false;
bool off_set = false;
for (const std::string& tt_line : truth_table) {
switch (tt_line.back()) {
case '1':
on_set = true;
break;
case '0':
off_set = true;
break;
default:
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s,[LINE%d])Invalid truth_table_line ending(=%c)!\n",
__FILE__, __LINE__, tt_line.back());
exit(1);
}
}
/* Prefer on_set if both are true */
if (true == on_set && true == off_set) {
on_set = true;
off_set = false;
}
return on_set;
}
/********************************************************************
* Complete a line in truth table with a given lut size
* Due to the size of truth table may be less than the lut size.
* i.e. in LUT-6 architecture, there exists LUT1-6 in technology-mapped netlists
* So, in truth table line, there may be 10- 1
* In this case, we should complete it by --10- 1
*******************************************************************/
static
std::string complete_truth_table_line(const size_t& lut_size,
const std::string& input_truth_table_line) {
std::string ret;
/* Split one line of truth table line*/
StringToken string_tokenizer(input_truth_table_line);
std::vector<std::string> tokens = string_tokenizer.split(' ');
/* Check, only 2 tokens*/
/* Sometimes, the truth table is ' 0' or ' 1', which corresponds to a constant */
if (1 == tokens.size()) {
/* restore the token[0]*/
tokens.insert(tokens.begin(), std::string("-"));
}
/* After processing, there should be 2 tokens. */
VTR_ASSERT(2 == tokens.size());
/* Complete the truth table line*/
size_t cover_len = tokens[0].length();
VTR_ASSERT( (cover_len < lut_size) || (cover_len == lut_size) );
/* Copy the original truth table line */
ret = tokens[0];
/* Add the number of '-' we should add in the back !!! */
for (size_t j = cover_len; j < lut_size; ++j) {
ret.push_back('-');
}
/* Copy the original truth table line */
ret.push_back(' ');
ret.append(tokens[1]);
/* Check if the size of ret matches our expectation */
VTR_ASSERT(lut_size + 2 == ret.size());
return ret;
}
/********************************************************************
* For each lut_bit_lines, we should recover the truth table,
* and then set the sram bits to "1" if the truth table defines so.
* Start_point: the position we start decode recursively
*******************************************************************/
static
void rec_build_lut_bitstream_per_line(std::vector<bool>& lut_bitstream,
const size_t& lut_size,
const std::string& truth_table_line,
const size_t& start_point) {
std::string temp_line(truth_table_line);
/* Check the length of sram bits and truth table line */
VTR_ASSERT(lut_size + 2 == truth_table_line.length()); /* lut_size + space + '1' */
/* End of truth_table_line should be "space" and "1" */
VTR_ASSERT( (0 == truth_table_line.compare(truth_table_line.length() - 2, 2, " 1"))
|| (0 == truth_table_line.compare(truth_table_line.length() - 2, 2, " 0")) );
/* Make sure before start point there is no '-' */
VTR_ASSERT(start_point < truth_table_line.length());
for (size_t i = 0; i < start_point; ++i) {
VTR_ASSERT('-' != truth_table_line[i]);
}
/* Configure sram bits recursively */
for (size_t i = start_point; i < lut_size; ++i) {
if ('-' == truth_table_line[i]) {
/* if we find a dont_care, we don't do configure now but recursively*/
/* '0' branch */
temp_line[i] = '0';
rec_build_lut_bitstream_per_line(lut_bitstream, lut_size, temp_line, start_point + 1);
/* '1' branch */
temp_line[i] = '1';
rec_build_lut_bitstream_per_line(lut_bitstream, lut_size, temp_line, start_point + 1);
return;
}
}
/* TODO: Use MuxGraph to decode this!!! */
/* Decode bitstream only when there are only 0 or 1 in the truth table */
size_t sram_id = 0;
for (size_t i = 0; i < lut_size; ++i) {
/* Should be either '0' or '1' */
switch (truth_table_line[i]) {
case '0':
/* We assume the 1-lut pass sram1 when input = 0 */
sram_id += (size_t)pow(2., (double)(i));
break;
case '1':
/* We assume the 1-lut pass sram0 when input = 1 */
break;
case '-':
default :
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid truth_table bit(%c), should be [0|1|]!\n",
__FILE__, __LINE__, truth_table_line[i]);
exit(1);
}
}
/* Set the sram bit to '1'*/
VTR_ASSERT(sram_id < lut_bitstream.size());
if (0 == truth_table_line.compare(truth_table_line.length() - 2, 2, " 1")) {
lut_bitstream[sram_id] = true; /* on set*/
} else if (0 == truth_table_line.compare(truth_table_line.length() - 2, 2, " 0")) {
lut_bitstream[sram_id] = false; /* off set */
} else {
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s, [LINE%d]) Invalid truth_table_line ending(=%s)!\n",
__FILE__, __LINE__, truth_table_line.substr(lut_size, 2));
exit(1);
}
}
/********************************************************************
* Generate the bitstream for a single-output LUT with a given truth table
* As truth tables may come from different logic blocks, truth tables could be in on and off sets
* We first build a base SRAM bits, where different parts are set to tbe on/off sets
* Then, we can decode SRAM bits as regular process
*******************************************************************/
static
std::vector<bool> build_single_output_lut_bitstream(const LutTruthTable& truth_table,
const MuxGraph& lut_mux_graph,
const size_t& default_sram_bit_value) {
size_t lut_size = lut_mux_graph.num_memory_bits();
size_t bitstream_size = lut_mux_graph.num_inputs();
std::vector<bool> lut_bitstream(bitstream_size, false);
LutTruthTable completed_truth_table;
bool on_set = false;
bool off_set = false;
/* if No truth_table, do default*/
if (0 == truth_table.size()) {
switch (default_sram_bit_value) {
case 0:
on_set = true;
off_set = false;
break;
case 1:
on_set = false;
off_set = true;
break;
default:
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s,[LINE%d]) Invalid default_signal_init_value(=%lu)!\n",
__FILE__, __LINE__, default_sram_bit_value);
exit(1);
}
} else {
on_set = lut_truth_table_use_on_set(truth_table);
off_set = !on_set;
}
/* Read in truth table lines, decode one by one */
for (const std::string& tt_line : truth_table) {
/* Complete the truth table line by line*/
completed_truth_table.push_back(complete_truth_table_line(lut_size, tt_line));
}
/* Initial all the bits in the bitstream */
if (true == on_set) {
lut_bitstream.resize(bitstream_size, false);
} else if (true == off_set) {
lut_bitstream.resize(bitstream_size, true);
}
for (const std::string& tt_line : completed_truth_table) {
/* Update the truth table, sram_bits */
rec_build_lut_bitstream_per_line(lut_bitstream, lut_size, tt_line, 0);
}
return lut_bitstream;
}
/********************************************************************
* Generate bitstream for a fracturable LUT (also applicable to single-output LUT)
* Check type of truth table of each mapped logical block
* if it is on-set, we give a all 0 base bitstream
* if it is off-set, we give a all 1 base bitstream
*******************************************************************/
std::vector<bool> build_frac_lut_bitstream(const CircuitLibrary& circuit_lib,
const MuxGraph& lut_mux_graph,
t_phy_pb* lut_pb,
const std::vector<LutTruthTable>& truth_tables,
const size_t& default_sram_bit_value) {
/* Initialization */
std::vector<bool> lut_bitstream(lut_mux_graph.num_inputs(), default_sram_bit_value);
for (int ilb = 0; ilb < lut_pb->num_logical_blocks; ++ilb) {
/* Find the corresponding circuit model output port and assoicated lut_output_mask */
CircuitPortId lut_model_output_port = lut_pb->lut_output_pb_graph_pin[ilb]->port->circuit_model_port;
size_t lut_frac_level = circuit_lib.port_lut_frac_level(lut_model_output_port);
/* Find the corresponding circuit model output port and assoicated lut_output_mask */
size_t lut_output_mask = circuit_lib.port_lut_output_masks(lut_model_output_port)[lut_pb->lut_output_pb_graph_pin[ilb]->pin_number];
/* Decode lut sram bits */
std::vector<bool> temp_bitstream = build_single_output_lut_bitstream(truth_tables[ilb], lut_mux_graph, default_sram_bit_value);
/* Depending on the frac-level, we get the location(starting/end points) of sram bits */
size_t length_of_temp_bitstream_to_copy = (size_t)pow(2., (double)(lut_frac_level));
size_t bitstream_offset = length_of_temp_bitstream_to_copy * lut_output_mask;
/* Ensure the offset is in range */
VTR_ASSERT(bitstream_offset < lut_bitstream.size());
VTR_ASSERT(bitstream_offset + length_of_temp_bitstream_to_copy <= lut_bitstream.size());
/* Copy to the segment of bitstream */
for (size_t bit = bitstream_offset; bit < bitstream_offset + length_of_temp_bitstream_to_copy; ++bit) {
lut_bitstream[bit] = temp_bitstream[bit];
}
}
return lut_bitstream;
}

View File

@ -0,0 +1,34 @@
/********************************************************************
* Header file for build_lut_bitstream.cpp
********************************************************************/
#ifndef BUILD_LUT_BITSTREAM_H
#define BUILD_LUT_BITSTREAM_H
#include <vector>
#include "circuit_library.h"
#include "mux_graph.h"
#include "vpr_types.h"
/* Alias name for data structure of LUT truth table */
typedef std::vector<std::string> LutTruthTable;
/* Declaration for functions */
LutTruthTable build_post_routing_wired_lut_truth_table(const int& lut_output_vpack_net_num,
const size_t& lut_size,
const std::vector<int>& lut_pin_vpack_net_num);
LutTruthTable build_post_routing_lut_truth_table(t_logical_block* mapped_logical_block,
const size_t& lut_size,
const std::vector<int>& lut_pin_vpack_net_num);
LutTruthTable adapt_truth_table_for_frac_lut(const CircuitLibrary& circuit_lib,
t_pb_graph_pin* lut_out_pb_graph_pin,
const LutTruthTable& truth_table);
std::vector<bool> build_frac_lut_bitstream(const CircuitLibrary& circuit_lib,
const MuxGraph& lut_mux_graph,
t_phy_pb* lut_pb,
const std::vector<LutTruthTable>& truth_tables,
const size_t& default_sram_bit_value);
#endif

View File

@ -277,7 +277,7 @@ void build_primitive_block_module(ModuleManager& module_manager,
CircuitModelId& primitive_model = primitive_pb_graph_node->pb_type->circuit_model;
/* Generate the module name for this primitive pb_graph_node*/
std::string primitive_module_name_prefix = generate_grid_block_prefix(std::string(grid_verilog_file_name_prefix), io_side);
std::string primitive_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), io_side);
std::string primitive_module_name = generate_physical_block_module_name(primitive_module_name_prefix, primitive_pb_graph_node->pb_type);
/* Create a module of the primitive LUT and register it to module manager */
@ -353,6 +353,8 @@ void build_primitive_block_module(ModuleManager& module_manager,
size_t memory_instance_id = module_manager.num_instance(primitive_module, memory_module);
/* Add the memory module as a child of primitive module */
module_manager.add_child_module(primitive_module, memory_module);
/* Set an instance name to bind to a block in bitstream generation */
module_manager.set_child_instance_name(primitive_module, memory_module, memory_instance_id, memory_module_name);
/* Add nets to connect regular and mode-select SRAM ports to the SRAM port of memory module */
add_module_nets_between_logic_and_memory_sram_bus(module_manager, primitive_module,
@ -557,6 +559,11 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
VTR_ASSERT(true == module_manager.valid_module_id(mux_mem_module));
size_t mux_mem_instance = module_manager.num_instance(pb_module, mux_mem_module);
module_manager.add_child_module(pb_module, mux_mem_module);
/* Give an instance name: this name should be consistent with the block name given in bitstream manager,
* If you want to bind the bitstream generation to modules
*/
std::string mux_mem_instance_name = generate_pb_memory_instance_name(GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""));
module_manager.set_child_instance_name(pb_module, mux_mem_module, mux_mem_instance, mux_mem_instance_name);
/* Add nets to connect SRAM ports of the MUX to the SRAM port of memory module */
add_module_nets_between_logic_and_memory_sram_bus(module_manager, pb_module,
@ -866,7 +873,7 @@ void rec_build_physical_block_modules(ModuleManager& module_manager,
}
/* Generate the name of the Verilog module for this pb_type */
std::string pb_module_name_prefix = generate_grid_block_prefix(std::string(grid_verilog_file_name_prefix), io_side);
std::string pb_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), io_side);
std::string pb_module_name = generate_physical_block_module_name(pb_module_name_prefix, physical_pb_type);
/* Register the Verilog module in module manager */
@ -994,12 +1001,12 @@ void build_grid_module(ModuleManager& module_manager,
border_side);
/* Create a Verilog Module for the top-level physical block, and add to module manager */
std::string grid_module_name = generate_grid_block_module_name(std::string(grid_verilog_file_name_prefix), std::string(phy_block_type->name), IO_TYPE == phy_block_type, border_side);
std::string grid_module_name = generate_grid_block_module_name(std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_block_type->name), IO_TYPE == phy_block_type, border_side);
ModuleId grid_module = module_manager.add_module(grid_module_name);
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
/* Generate the name of the Verilog module for this pb_type */
std::string pb_module_name_prefix(grid_verilog_file_name_prefix);
std::string pb_module_name_prefix(GRID_MODULE_NAME_PREFIX);
std::string pb_module_name = generate_grid_physical_block_module_name(pb_module_name_prefix, phy_block_type->pb_graph_head->pb_type, border_side);
ModuleId pb_module = module_manager.find_module(pb_module_name);
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));

View File

@ -10,6 +10,7 @@
#include "vpr_types.h"
#include "globals.h"
#include "fpga_x2p_reserved_words.h"
#include "fpga_x2p_naming.h"
#include "fpga_x2p_utils.h"
#include "fpga_x2p_pbtypes_utils.h"
@ -95,7 +96,7 @@ size_t add_top_module_grid_instance(ModuleManager& module_manager,
const e_side& border_side,
const vtr::Point<size_t>& grid_coord) {
/* Find the module name for this type of grid */
std::string grid_module_name_prefix(grid_verilog_file_name_prefix);
std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX);
std::string grid_module_name = generate_grid_block_module_name(grid_module_name_prefix, std::string(grid_type->name), IO_TYPE == grid_type, border_side);
ModuleId grid_module = module_manager.find_module(grid_module_name);
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
@ -404,7 +405,7 @@ void add_top_module_nets_connect_grids_and_sb(ModuleManager& module_manager,
/* Collect source-related information */
/* Generate the grid module name by considering if it locates on the border */
vtr::Point<size_t> grid_coordinate(rr_gsb.get_opin_node(side_manager.get_side(), inode)->xlow, (rr_gsb.get_opin_node(side_manager.get_side(), inode)->ylow));
std::string src_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(grid_verilog_file_name_prefix), device_size, grids, grid_coordinate);
std::string src_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), device_size, grids, grid_coordinate);
ModuleId src_grid_module = module_manager.find_module(src_grid_module_name);
VTR_ASSERT(true == module_manager.valid_module_id(src_grid_module));
size_t src_grid_instance = grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()];
@ -556,7 +557,7 @@ void add_top_module_nets_connect_grids_and_cb(ModuleManager& module_manager,
*/
t_rr_node* instance_ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode);
vtr::Point<size_t> grid_coordinate(instance_ipin_node->xlow, instance_ipin_node->ylow);
std::string sink_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(grid_verilog_file_name_prefix), device_size, grids, grid_coordinate);
std::string sink_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), device_size, grids, grid_coordinate);
ModuleId sink_grid_module = module_manager.find_module(sink_grid_module_name);
VTR_ASSERT(true == module_manager.valid_module_id(sink_grid_module));
size_t sink_grid_instance = grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()];