start refactoring bitstream generator
This commit is contained in:
parent
13c62fdcf8
commit
838173f3c4
|
@ -1,6 +1,8 @@
|
|||
#ifndef LINKEDLIST_H
|
||||
#define LINKEDLIST_H
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/*General Purpose Linked List*/
|
||||
typedef struct s_llist t_llist;
|
||||
struct s_llist
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* data structures in mux_graph.h
|
||||
*************************************************/
|
||||
#include <cmath>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -482,6 +483,21 @@ MuxNodeId MuxGraph::node_id(const MuxInputId& input_id) const {
|
|||
return MuxNodeId::INVALID();
|
||||
}
|
||||
|
||||
/* Get the node id of a given output */
|
||||
MuxNodeId MuxGraph::node_id(const MuxOutputId& output_id) const {
|
||||
/* Use the node_lookup to accelerate the search */
|
||||
for (const auto& lvl : node_lookup_) {
|
||||
for (const auto& cand_node : lvl[MUX_OUTPUT_NODE]) {
|
||||
if (output_id == node_output_ids_[cand_node]) {
|
||||
return cand_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MuxNodeId::INVALID();
|
||||
}
|
||||
|
||||
|
||||
/* Get the node id w.r.t. the node level and node_index at the level
|
||||
* Return an invalid value if not found
|
||||
*/
|
||||
|
@ -516,19 +532,41 @@ MuxNodeId MuxGraph::node_id(const size_t& node_level, const size_t& node_index_a
|
|||
return ret_node;
|
||||
}
|
||||
|
||||
/* Decode memory bits based on an input id */
|
||||
std::vector<size_t> MuxGraph::decode_memory_bits(const MuxInputId& input_id) const {
|
||||
/* Decode memory bits based on an input id and an output id */
|
||||
std::vector<bool> MuxGraph::decode_memory_bits(const MuxInputId& input_id,
|
||||
const MuxOutputId& output_id) const {
|
||||
/* initialize the memory bits: TODO: support default value */
|
||||
std::vector<size_t> mem_bits(mem_ids_.size(), 0);
|
||||
std::vector<bool> mem_bits(mem_ids_.size(), false);
|
||||
|
||||
/* valid the input */
|
||||
/* valid the input and output */
|
||||
VTR_ASSERT_SAFE(valid_input_id(input_id));
|
||||
VTR_ASSERT_SAFE(valid_output_id(output_id));
|
||||
|
||||
/* Route the input to the output and update mem */
|
||||
MuxNodeId next_node = node_id(input_id);
|
||||
while ( 0 < node_out_edges_[next_node].size() ) {
|
||||
VTR_ASSERT_SAFE (1 == node_out_edges_[next_node].size());
|
||||
MuxEdgeId edge = node_out_edges_[next_node][0];
|
||||
/* Mark all the nodes as not visited */
|
||||
vtr::vector<MuxNodeId, bool> visited(nodes().size(), false);
|
||||
|
||||
/* Create a queue for Breadth-First Search */
|
||||
std::list<MuxNodeId> queue;
|
||||
|
||||
/* Mark the input node as visited and enqueue it */
|
||||
visited[node_id(input_id)] = true;
|
||||
queue.push_back(node_id(input_id));
|
||||
|
||||
/* Create a flag to indicate if the route is success or not */
|
||||
bool route_success = false;
|
||||
|
||||
while(!queue.empty()) {
|
||||
/* Dequeue a mux node from queue,
|
||||
* we will walk through all the fan-in of this node in this loop
|
||||
*/
|
||||
MuxNodeId node_to_expand = queue.front();
|
||||
queue.pop_front();
|
||||
/* Get all fan-in nodes of the dequeued node
|
||||
* If the node has not been visited,
|
||||
* then mark it visited and enqueue it
|
||||
*/
|
||||
VTR_ASSERT_SAFE (1 == node_out_edges_[node_to_expand].size());
|
||||
MuxEdgeId edge = node_out_edges_[node_to_expand][0];
|
||||
|
||||
/* Configure the mem bits:
|
||||
* if inv_mem is enabled, it means 0 to enable this edge
|
||||
|
@ -537,25 +575,113 @@ std::vector<size_t> MuxGraph::decode_memory_bits(const MuxInputId& input_id) con
|
|||
MuxMemId mem = edge_mem_ids_[edge];
|
||||
VTR_ASSERT_SAFE (valid_mem_id(mem));
|
||||
if (true == edge_inv_mem_[edge]) {
|
||||
mem_bits[size_t(mem)] = 0;
|
||||
mem_bits[size_t(mem)] = false;
|
||||
} else {
|
||||
mem_bits[size_t(mem)] = 1;
|
||||
mem_bits[size_t(mem)] = true;
|
||||
}
|
||||
|
||||
/* each edge must have 1 fan-out */
|
||||
VTR_ASSERT_SAFE (1 == edge_sink_nodes_[edge].size());
|
||||
|
||||
/* Visit the next node */
|
||||
next_node = edge_sink_nodes_[edge][0];
|
||||
/* Get the fan-out node */
|
||||
MuxNodeId next_node = edge_sink_nodes_[edge][0];
|
||||
|
||||
/* If next node is the output node we want, we can finish here */
|
||||
if (next_node == node_id(output_id)) {
|
||||
route_success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add next node to the queue if not visited yet */
|
||||
if (false == visited[next_node]) {
|
||||
visited[next_node] = true;
|
||||
queue.push_back(next_node);
|
||||
}
|
||||
}
|
||||
|
||||
/* valid the output */
|
||||
VTR_ASSERT_SAFE(MUX_OUTPUT_NODE == node_types_[next_node]);
|
||||
VTR_ASSERT_SAFE(valid_output_id(node_output_ids_[next_node]));
|
||||
/* Routing must be success! */
|
||||
VTR_ASSERT(true == route_success);
|
||||
|
||||
return mem_bits;
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
MuxInputId MuxGraph::find_input_node_driven_by_output_node(const std::map<MuxMemId, bool>& memory_bits,
|
||||
const MuxOutputId& output_id) const {
|
||||
/* Ensure that the memory bits fit the size of memory bits in this MUX */
|
||||
VTR_ASSERT(memory_bits.size() == mem_ids_.size());
|
||||
|
||||
/* valid the output */
|
||||
VTR_ASSERT_SAFE(valid_output_id(output_id));
|
||||
|
||||
/* Start from the output node */
|
||||
/* Mark all the nodes as not visited */
|
||||
vtr::vector<MuxNodeId, bool> visited(nodes().size(), false);
|
||||
|
||||
/* Create a queue for Breadth-First Search */
|
||||
std::list<MuxNodeId> queue;
|
||||
|
||||
/* Mark the output node as visited and enqueue it */
|
||||
visited[node_id(output_id)] = true;
|
||||
queue.push_back(node_id(output_id));
|
||||
|
||||
/* Record the destination input id */
|
||||
MuxInputId des_input_id = MuxInputId::INVALID();
|
||||
|
||||
while(!queue.empty()) {
|
||||
/* Dequeue a mux node from queue,
|
||||
* we will walk through all the fan-in of this node in this loop
|
||||
*/
|
||||
MuxNodeId node_to_expand = queue.front();
|
||||
queue.pop_front();
|
||||
/* Get all fan-in nodes of the dequeued node
|
||||
* If the node has not been visited,
|
||||
* then mark it visited and enqueue it
|
||||
*/
|
||||
MuxEdgeId next_edge = MuxEdgeId::INVALID();
|
||||
for (const MuxEdgeId& edge : node_in_edges_[node_to_expand]) {
|
||||
/* Configure the mem bits and find the edge that will propagate the signal
|
||||
* if inv_mem is enabled, it means false to enable this edge
|
||||
* otherwise, it is true to enable this edge
|
||||
*/
|
||||
MuxMemId mem = edge_mem_ids_[edge];
|
||||
VTR_ASSERT_SAFE (valid_mem_id(mem));
|
||||
if (edge_inv_mem_[edge] == !memory_bits.at(mem)) {
|
||||
next_edge = edge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* We must have a valid next edge */
|
||||
VTR_ASSERT(MuxEdgeId::INVALID() != next_edge);
|
||||
|
||||
/* each edge must have 1 fan-out */
|
||||
VTR_ASSERT_SAFE (1 == edge_src_nodes_[next_edge].size());
|
||||
|
||||
/* Get the fan-in node */
|
||||
MuxNodeId next_node = edge_src_nodes_[next_edge][0];
|
||||
|
||||
/* If next node is an input node, we can finish here */
|
||||
if (true == is_node_input(next_node)) {
|
||||
des_input_id = input_id(next_node);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add next node to the queue if not visited yet */
|
||||
if (false == visited[next_node]) {
|
||||
visited[next_node] = true;
|
||||
queue.push_back(next_node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Routing must be success! */
|
||||
VTR_ASSERT(MuxInputId::INVALID() != des_input_id);
|
||||
|
||||
return des_input_id;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Private mutators: basic operations
|
||||
*************************************************/
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#ifndef MUX_GRAPH_H
|
||||
#define MUX_GRAPH_H
|
||||
|
||||
#include <map>
|
||||
#include "vtr_vector.h"
|
||||
#include "vtr_range.h"
|
||||
#include "mux_graph_fwd.h"
|
||||
|
@ -105,6 +106,8 @@ class MuxGraph {
|
|||
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 */
|
||||
|
@ -115,8 +118,19 @@ class MuxGraph {
|
|||
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 */
|
||||
std::vector<size_t> decode_memory_bits(const MuxInputId& input_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
|
||||
*/
|
||||
std::vector<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);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "mux_library_builder.h"
|
||||
#include "build_module_graph.h"
|
||||
#include "build_device_bitstream.h"
|
||||
|
||||
#include "spice_api.h"
|
||||
#include "verilog_api.h"
|
||||
|
@ -82,6 +83,18 @@ void vpr_fpga_x2p_tool_suites(t_vpr_setup vpr_setup,
|
|||
device_size, grids,
|
||||
rr_switches, clb2clb_directs, device_rr_gsb);
|
||||
|
||||
/* Build bitstream database if needed */
|
||||
BitstreamManager bitstream_manager;
|
||||
if ((TRUE == vpr_setup.FPGA_SPICE_Opts.BitstreamGenOpts.gen_bitstream)
|
||||
&&(FALSE == vpr_setup.FPGA_SPICE_Opts.SpiceOpts.do_spice)
|
||||
&&(FALSE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.dump_syn_verilog)) {
|
||||
bitstream_manager = build_device_bitstream(vpr_setup, Arch, module_manager,
|
||||
Arch.spice->circuit_lib, mux_lib,
|
||||
device_size, grids,
|
||||
rr_switches, rr_node, device_rr_gsb);
|
||||
|
||||
}
|
||||
|
||||
/* Xifan TANG: SPICE Modeling, SPICE Netlist Output */
|
||||
if (TRUE == vpr_setup.FPGA_SPICE_Opts.SpiceOpts.do_spice) {
|
||||
vpr_fpga_spice(vpr_setup, Arch, vpr_setup.FileNameOpts.CircuitName);
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to build bitstream from a mapped
|
||||
* FPGA fabric.
|
||||
* We decode the bitstream from configuration of routing multiplexers
|
||||
* and Look-Up Tables (LUTs) which locate in CLBs and global routing architecture
|
||||
*******************************************************************/
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "fpga_x2p_naming.h"
|
||||
|
||||
#include "build_routing_bitstream.h"
|
||||
#include "build_device_bitstream.h"
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function to build a bistream from the FPGA device
|
||||
* 1. It will organize the bitstream w.r.t. the hierarchy of module graphs
|
||||
* describing the FPGA fabric
|
||||
* 2. It will decode configuration bits from routing multiplexers used in
|
||||
* global routing architecture
|
||||
* 3. It will decode configuration bits from routing multiplexers and LUTs
|
||||
* used in CLBs
|
||||
*
|
||||
* Note: this function create a bitstream which is binding to the module graphs
|
||||
* of the FPGA fabric that FPGA-X2P generates!
|
||||
* But it can be used to output a generic bitstream for VPR mapping FPGA
|
||||
*******************************************************************/
|
||||
BitstreamManager build_device_bitstream(const t_vpr_setup& vpr_setup,
|
||||
const t_arch& arch,
|
||||
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,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
t_rr_node* L_rr_node,
|
||||
const DeviceRRGSB& L_device_rr_gsb) {
|
||||
/* Check if the routing architecture we support*/
|
||||
if (UNI_DIRECTIONAL != vpr_setup.RoutingArch.directionality) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"FPGA X2P only supports uni-directional routing architecture!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* We don't support mrFPGA */
|
||||
#ifdef MRFPGA_H
|
||||
if (is_mrFPGA) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"FPGA X2P does not support mrFPGA!\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Bistream builder formally starts*/
|
||||
vpr_printf(TIO_MESSAGE_INFO, "\nStart building bitstream for FPGA fabric...\n");
|
||||
|
||||
/* Bitstream manager to be built */
|
||||
BitstreamManager bitstream_manager;
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Assign the SRAM model applied to the FPGA fabric */
|
||||
VTR_ASSERT(NULL != arch.sram_inf.verilog_sram_inf_orgz); /* Check !*/
|
||||
t_spice_model* mem_model = arch.sram_inf.verilog_sram_inf_orgz->spice_model;
|
||||
/* initialize the SRAM organization information struct */
|
||||
CircuitModelId sram_model = arch.spice->circuit_lib.model(mem_model->name);
|
||||
VTR_ASSERT(CircuitModelId::INVALID() != sram_model);
|
||||
|
||||
/* Create the top-level block for bitstream
|
||||
* This is related to the top-level module of fpga
|
||||
*/
|
||||
std::string top_block_name = generate_fpga_top_module_name();
|
||||
ConfigBlockId top_block = bitstream_manager.add_block(top_block_name);
|
||||
|
||||
/* Create bitstream from routing architectures */
|
||||
build_routing_bitstream(bitstream_manager, module_manager, circuit_lib, mux_lib, rr_switches, L_rr_node, L_device_rr_gsb);
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Building bitstream took %g seconds\n",
|
||||
run_time_sec);
|
||||
|
||||
return bitstream_manager;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/********************************************************************
|
||||
* Header file for build_device_bitstream.cpp
|
||||
*******************************************************************/
|
||||
#ifndef BUILD_DEVICE_BITSTREAM_H
|
||||
#define BUILD_DEVICE_BITSTREAM_H
|
||||
|
||||
#include <vector>
|
||||
#include "bitstream_manager.h"
|
||||
#include "vpr_types.h"
|
||||
#include "module_manager.h"
|
||||
#include "circuit_library.h"
|
||||
#include "mux_library.h"
|
||||
#include "rr_blocks.h"
|
||||
|
||||
BitstreamManager build_device_bitstream(const t_vpr_setup& vpr_setup,
|
||||
const t_arch& arch,
|
||||
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,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
t_rr_node* L_rr_node,
|
||||
const DeviceRRGSB& L_device_rr_gsb);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,105 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to build bitstream from routing multiplexers
|
||||
* which are based on different technology
|
||||
*******************************************************************/
|
||||
#include "vtr_assert.h"
|
||||
|
||||
#include "mux_utils.h"
|
||||
#include "fpga_x2p_types.h"
|
||||
|
||||
#include "build_mux_bitstream.h"
|
||||
|
||||
/********************************************************************
|
||||
* Find the default path id of a MUX
|
||||
* This is applied when the path id specified is DEFAULT_PATH_ID,
|
||||
* which is not correlated to the MUX implementation
|
||||
* This function is binding the default path id to the implemented structure
|
||||
* 1. If the MUX has a constant input, the default path id will be
|
||||
* directed to the last input of the MUX, which is the constant input
|
||||
* 2. If the MUX does not have a constant input, the default path id
|
||||
* will the first input of the MUX.
|
||||
*
|
||||
* Restriction:
|
||||
* we assume the default path is the first input of the MUX
|
||||
* Change if this is not what you want
|
||||
*******************************************************************/
|
||||
size_t find_mux_default_path_id(const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& mux_model,
|
||||
const size_t& mux_size) {
|
||||
size_t default_path_id;
|
||||
|
||||
if (TRUE == circuit_lib.mux_add_const_input(mux_model)) {
|
||||
default_path_id = mux_size; /* When there is a constant input, use the last path */
|
||||
} else {
|
||||
default_path_id = DEFAULT_MUX_PATH_ID; /* When there is no constant input, use the default one */
|
||||
}
|
||||
|
||||
return default_path_id;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function generates bitstream for a CMOS routing multiplexer
|
||||
* Thanks to MuxGraph object has already describe the internal multiplexing
|
||||
* structure, bitstream generation is simply done by routing the signal
|
||||
* to from a given input to the output
|
||||
* All the memory bits can be generated by an API of MuxGraph
|
||||
*
|
||||
* To be generic, this function only returns a vector bit values
|
||||
* without touching an bitstream-relate data structure
|
||||
*******************************************************************/
|
||||
static
|
||||
std::vector<bool> build_cmos_mux_bitstream(const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& mux_model,
|
||||
const MuxLibrary& mux_lib,
|
||||
const size_t& mux_size,
|
||||
const int& path_id) {
|
||||
/* Note that the size of implemented mux could be different than the mux size we see here,
|
||||
* due to the constant inputs
|
||||
* We will find the input size of implemented MUX and fetch the graph-based representation in MUX library
|
||||
*/
|
||||
size_t implemented_mux_size = find_mux_implementation_num_inputs(circuit_lib, mux_model, mux_size);
|
||||
MuxId mux_graph_id = mux_lib.mux_graph(mux_model, implemented_mux_size);
|
||||
const MuxGraph mux_graph = mux_lib.mux_graph(mux_graph_id);
|
||||
|
||||
size_t datapath_id = path_id;
|
||||
|
||||
/* Find the path_id related to the implementation */
|
||||
if (DEFAULT_PATH_ID == path_id) {
|
||||
datapath_id = find_mux_default_path_id(circuit_lib, mux_model, implemented_mux_size);
|
||||
} else {
|
||||
VTR_ASSERT( datapath_id < mux_size);
|
||||
}
|
||||
|
||||
/* We should have only one output for this MUX! */
|
||||
VTR_ASSERT(1 == mux_graph.outputs().size());
|
||||
|
||||
/* Generate the memory bits */
|
||||
return mux_graph.decode_memory_bits(MuxInputId(datapath_id), mux_graph.output_id(mux_graph.outputs()[0]));
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function generates bitstream for a routing multiplexer
|
||||
* supporting both CMOS and ReRAM multiplexer designs
|
||||
*******************************************************************/
|
||||
std::vector<bool> build_mux_bitstream(const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& mux_model,
|
||||
const MuxLibrary& mux_lib,
|
||||
const size_t& mux_size,
|
||||
const int& path_id) {
|
||||
std::vector<bool> mux_bitstream;
|
||||
|
||||
switch (circuit_lib.design_tech_type(mux_model)) {
|
||||
case SPICE_MODEL_DESIGN_CMOS:
|
||||
mux_bitstream = build_cmos_mux_bitstream(circuit_lib, mux_model, mux_lib, mux_size, path_id);
|
||||
break;
|
||||
case SPICE_MODEL_DESIGN_RRAM:
|
||||
/* TODO: ReRAM MUX needs a different bitstream generation strategy */
|
||||
break;
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s,[LINE%d])Invalid design technology for circuit model (%s)!\n",
|
||||
__FILE__, __LINE__, circuit_lib.model_name(mux_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
return mux_bitstream;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/********************************************************************
|
||||
* Header file for build_mux_bitstream.cpp
|
||||
*******************************************************************/
|
||||
#ifndef BUILD_MUX_BITSTREAM_H
|
||||
#define BUILD_MUX_BITSTREAM_H
|
||||
|
||||
#include <vector>
|
||||
#include "circuit_library.h"
|
||||
#include "mux_library.h"
|
||||
|
||||
size_t find_mux_default_path_id(const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& mux_model,
|
||||
const size_t& mux_size);
|
||||
|
||||
std::vector<bool> build_mux_bitstream(const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& mux_model,
|
||||
const MuxLibrary& mux_lib,
|
||||
const size_t& mux_size,
|
||||
const int& path_id);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,214 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to build bitstream from global routing
|
||||
* architecture of a mapped FPGA fabric
|
||||
* We decode the bitstream from configuration of routing multiplexers
|
||||
* which locate in global routing architecture
|
||||
*******************************************************************/
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "util.h"
|
||||
#include "mux_utils.h"
|
||||
#include "fpga_x2p_types.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
|
||||
#include "build_mux_bitstream.h"
|
||||
#include "build_routing_bitstream.h"
|
||||
|
||||
/********************************************************************
|
||||
* This function generates bitstream for a routing multiplexer
|
||||
* This function will identify if a node indicates a routing multiplexer
|
||||
* If not a routing multiplexer, no bitstream is needed here
|
||||
* If yes, we will generate the bitstream for the routing multiplexer
|
||||
*******************************************************************/
|
||||
static
|
||||
void build_switch_block_mux_bitstream(BitstreamManager& bitstream_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const MuxLibrary& mux_lib,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
t_rr_node* L_rr_node,
|
||||
const RRGSB& rr_gsb,
|
||||
t_rr_node* cur_rr_node,
|
||||
const std::vector<t_rr_node*>& drive_rr_nodes,
|
||||
const int& switch_index) {
|
||||
/* Check current rr_node is CHANX or CHANY*/
|
||||
VTR_ASSERT((CHANX == cur_rr_node->type)||(CHANY == cur_rr_node->type));
|
||||
|
||||
/* Find the circuit model id of the mux, we need its design technology which matters the bitstream generation */
|
||||
CircuitModelId mux_model = rr_switches[switch_index].circuit_model;
|
||||
|
||||
/* Find the input size of the implementation of a routing multiplexer */
|
||||
size_t datapath_mux_size = drive_rr_nodes.size();
|
||||
|
||||
/* Find out which routing path is used in this MUX */
|
||||
int path_id = DEFAULT_PATH_ID;
|
||||
for (size_t inode = 0; inode < drive_rr_nodes.size(); ++inode) {
|
||||
if (drive_rr_nodes[inode] == &(L_rr_node[cur_rr_node->prev_node])) {
|
||||
path_id = (int)inode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure that our path id makes sense! */
|
||||
VTR_ASSERT( (DEFAULT_PATH_ID == path_id)
|
||||
|| ( (DEFAULT_PATH_ID < path_id) && (path_id < (int)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);
|
||||
|
||||
/* Add the bistream to the bitstream manager */
|
||||
for (const bool& bit : mux_bitstream) {
|
||||
bitstream_manager.add_bit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function generates bitstream for an interconnection,
|
||||
* i.e., a routing multiplexer, in a Switch Block
|
||||
* This function will identify if a node indicates a routing multiplexer
|
||||
* If not a routing multiplexer, no bitstream is needed here
|
||||
* If yes, we will generate the bitstream for the routing multiplexer
|
||||
*******************************************************************/
|
||||
static
|
||||
void build_switch_block_interc_bitstream(BitstreamManager& bitstream_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const MuxLibrary& mux_lib,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
t_rr_node* L_rr_node,
|
||||
const RRGSB& rr_gsb,
|
||||
const e_side& chan_side,
|
||||
const size_t& chan_node_id) {
|
||||
|
||||
std::vector<t_rr_node*> drive_rr_nodes;
|
||||
|
||||
/* Get the node */
|
||||
t_rr_node* cur_rr_node = rr_gsb.get_chan_node(chan_side, chan_node_id);
|
||||
|
||||
/* Determine if the interc lies inside a channel wire, that is interc between segments */
|
||||
if (false == rr_gsb.is_sb_node_passing_wire(chan_side, chan_node_id)) {
|
||||
for (int i = 0; i < cur_rr_node->num_drive_rr_nodes; ++i) {
|
||||
drive_rr_nodes.push_back(cur_rr_node->drive_rr_nodes[i]);
|
||||
}
|
||||
/* Special: if there are zero-driver nodes. We skip here */
|
||||
if (0 == drive_rr_nodes.size()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (0 == drive_rr_nodes.size())
|
||||
|| (0 == drive_rr_nodes.size()) ) {
|
||||
/* No bitstream generation required by a special direct connection*/
|
||||
return;
|
||||
} else if (1 < drive_rr_nodes.size()) {
|
||||
/* This is a routing multiplexer! Generate bitstream */
|
||||
build_switch_block_mux_bitstream(bitstream_manager, module_manager,
|
||||
circuit_lib, mux_lib, rr_switches, L_rr_node,
|
||||
rr_gsb, cur_rr_node, drive_rr_nodes,
|
||||
cur_rr_node->drive_switches[DEFAULT_SWITCH_ID]);
|
||||
} /*Nothing should be done else*/
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function generates bitstream for a Switch Block
|
||||
* and add it to the bitstream manager
|
||||
* This function will spot all the routing multiplexers in a Switch Block
|
||||
* using a simple but effective rule:
|
||||
* The fan-in of each output node.
|
||||
* If there are more than 2 fan-in, there is a routing multiplexer
|
||||
*
|
||||
* Note that the output nodes typically spread over all the sides of a Switch Block
|
||||
* So, we will iterate over that.
|
||||
*******************************************************************/
|
||||
static
|
||||
void build_switch_block_bitstream(BitstreamManager& bitstream_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const MuxLibrary& mux_lib,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
t_rr_node* L_rr_node,
|
||||
const RRGSB& rr_sb) {
|
||||
/* TODO: Create a block for the bitstream which corresponds to the Switch block */
|
||||
|
||||
/* Iterate over all the multiplexers */
|
||||
for (size_t side = 0; side < rr_sb.get_num_sides(); ++side) {
|
||||
Side side_manager(side);
|
||||
for (size_t itrack = 0; itrack < rr_sb.get_chan_width(side_manager.get_side()); ++itrack) {
|
||||
VTR_ASSERT( (CHANX == rr_sb.get_chan_node(side_manager.get_side(), itrack)->type)
|
||||
|| (CHANY == rr_sb.get_chan_node(side_manager.get_side(), itrack)->type));
|
||||
/* Only output port indicates a routing multiplexer */
|
||||
if (OUT_PORT != rr_sb.get_chan_node_direction(side_manager.get_side(), itrack)) {
|
||||
continue;
|
||||
}
|
||||
build_switch_block_interc_bitstream(bitstream_manager, module_manager,
|
||||
circuit_lib, mux_lib, rr_switches, L_rr_node,
|
||||
rr_sb, side_manager.get_side(), itrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function to create bitstream for global routing architecture
|
||||
* Two major tasks:
|
||||
* 1. Generate bitstreams for Switch Blocks
|
||||
* 2. Generate bitstreams for both X-direction and Y-direction Connection Blocks
|
||||
*******************************************************************/
|
||||
void build_routing_bitstream(BitstreamManager& bitstream_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const MuxLibrary& mux_lib,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
t_rr_node* L_rr_node,
|
||||
const DeviceRRGSB& L_device_rr_gsb) {
|
||||
|
||||
/* Generate bitstream for each switch blocks
|
||||
* To organize the bitstream in blocks, we create a block for each switch block
|
||||
* and give names which are same as they are in top-level module managers
|
||||
*/
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating bitstream for Switch blocks...\n");
|
||||
DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range();
|
||||
for (size_t ix = 0; ix < sb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < sb_range.get_y(); ++iy) {
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
build_switch_block_bitstream(bitstream_manager, module_manager,
|
||||
circuit_lib, mux_lib, rr_switches, L_rr_node,
|
||||
rr_gsb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate bitstream for each connection blocks
|
||||
* To organize the bitstream in blocks, we create a block for each connection block
|
||||
* and give names which are same as they are in top-level module managers
|
||||
*/
|
||||
DeviceCoordinator cb_range = L_device_rr_gsb.get_gsb_range();
|
||||
vpr_printf(TIO_MESSAGE_INFO,"Generating bitstream for Connection blocks ...\n");
|
||||
|
||||
for (size_t ix = 0; ix < cb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < cb_range.get_y(); ++iy) {
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
/* X - channels [1...nx][0..ny]*/
|
||||
if ((TRUE == is_cb_exist(CHANX, ix, iy))
|
||||
&&(true == rr_gsb.is_cb_exist(CHANX))) {
|
||||
/*
|
||||
fpga_spice_generate_bitstream_routing_connection_box_subckt(fp,
|
||||
rr_gsb, CHANX,
|
||||
cur_sram_orgz_info);
|
||||
*/
|
||||
}
|
||||
/* Y - channels [1...ny][0..nx]*/
|
||||
if ((TRUE == is_cb_exist(CHANY, ix, iy))
|
||||
&&(true == rr_gsb.is_cb_exist(CHANY))) {
|
||||
/*
|
||||
fpga_spice_generate_bitstream_routing_connection_box_subckt(fp,
|
||||
rr_gsb, CHANY,
|
||||
cur_sram_orgz_info);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/********************************************************************
|
||||
* Header file for build_routing_bitstream.cpp
|
||||
*******************************************************************/
|
||||
#ifndef BUILD_ROUTING_BITSTREAM_H
|
||||
#define BUILD_ROUTING_BITSTREAM_H
|
||||
|
||||
#include <vector>
|
||||
#include "bitstream_manager.h"
|
||||
#include "vpr_types.h"
|
||||
#include "module_manager.h"
|
||||
#include "circuit_library.h"
|
||||
#include "mux_library.h"
|
||||
#include "rr_blocks.h"
|
||||
|
||||
void build_routing_bitstream(BitstreamManager& bitstream_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const MuxLibrary& mux_lib,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
t_rr_node* L_rr_node,
|
||||
const DeviceRRGSB& L_device_rr_gsb);
|
||||
|
||||
#endif
|
|
@ -51,7 +51,8 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup,
|
|||
#endif
|
||||
|
||||
/* Module Graph builder formally starts*/
|
||||
vpr_printf(TIO_MESSAGE_INFO, "\nStart building module graphs for FPGA fabric...\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"\nStart building module graphs for FPGA fabric...\n");
|
||||
|
||||
/* Module manager to be built */
|
||||
ModuleManager module_manager;
|
||||
|
@ -135,8 +136,9 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup,
|
|||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO, "Building module graphs took %g seconds\n", run_time_sec);
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Building module graphs took %g seconds\n",
|
||||
run_time_sec);
|
||||
|
||||
return module_manager;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue