bring bitstream generator for routing modules online

This commit is contained in:
tangxifan 2020-02-23 22:09:46 -07:00
parent 86c7c24701
commit 712eeb1340
5 changed files with 508 additions and 8 deletions

View File

@ -14,7 +14,7 @@
#include "openfpga_naming.h"
//#include "build_grid_bitstream.h"
//#include "build_routing_bitstream.h"
#include "build_routing_bitstream.h"
#include "build_device_bitstream.h"
/* begin namespace openfpga */
@ -60,9 +60,18 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx,
/* Create bitstream from routing architectures */
VTR_LOGV(verbose, "Building routing bitstream...\n");
//build_routing_bitstream(bitstream_manager, top_block, module_manager, circuit_lib, mux_lib, rr_switches, L_rr_node, L_device_rr_gsb);
build_routing_bitstream(bitstream_manager, top_block,
openfpga_ctx.module_graph(),
openfpga_ctx.arch().circuit_lib,
openfpga_ctx.mux_lib(),
openfpga_ctx.vpr_device_annotation(),
openfpga_ctx.vpr_routing_annotation(),
vpr_ctx.device().rr_graph,
openfpga_ctx.device_rr_gsb());
VTR_LOGV(verbose, "Done\n");
VTR_LOGV(verbose, "Decoded %lu configuration bits\n", bitstream_manager.bits().size());
return bitstream_manager;
}

View File

@ -13,17 +13,12 @@
#include "mux_utils.h"
#include "decoder_library_utils.h"
#include "mux_bitstream_constants.h"
#include "build_mux_bitstream.h"
/* begin namespace openfpga */
namespace openfpga {
/* Default path ID of a unused multiplexer */
#define DEFAULT_PATH_ID -1
/* Default path ID of a unused multiplexer when there are no constant inputs*/
#define DEFAULT_MUX_PATH_ID 0
/********************************************************************
* Find the default path id of a MUX
* This is applied when the path id specified is DEFAULT_PATH_ID,

View File

@ -0,0 +1,442 @@
/********************************************************************
* 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>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgautil library */
#include "openfpga_side_manager.h"
#include "mux_utils.h"
#include "rr_gsb_utils.h"
#include "openfpga_reserved_words.h"
#include "openfpga_naming.h"
#include "openfpga_rr_graph_utils.h"
#include "mux_bitstream_constants.h"
#include "build_mux_bitstream.h"
#include "build_routing_bitstream.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* 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 ConfigBlockId& mux_mem_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const RRGraph& rr_graph,
const RRNodeId& cur_rr_node,
const std::vector<RRNodeId>& drive_rr_nodes,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation) {
/* Check current rr_node is CHANX or CHANY*/
VTR_ASSERT( (CHANX == rr_graph.node_type(cur_rr_node))
|| (CHANY == rr_graph.node_type(cur_rr_node)));
/* 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 (routing_annotation.rr_node_net(drive_rr_nodes[inode]) == routing_annotation.rr_node_net(cur_rr_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) )
);
/* Find the circuit model id of the mux, we need its design technology which matters the bitstream generation */
std::vector<RRSwitchId> driver_switches = get_rr_graph_driver_switches(rr_graph, cur_rr_node);
VTR_ASSERT(1 == driver_switches.size());
CircuitModelId mux_model = device_annotation.rr_switch_circuit_model(driver_switches[0]);
/* 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);
/* 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);
}
}
/********************************************************************
* 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 ConfigBlockId& sb_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const RRGraph& rr_graph,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGSB& rr_gsb,
const e_side& chan_side,
const size_t& chan_node_id) {
std::vector<RRNodeId> driver_rr_nodes;
/* Get the node */
const RRNodeId& 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(rr_graph, chan_side, chan_node_id)) {
driver_rr_nodes = get_rr_graph_configurable_driver_nodes(rr_graph, cur_rr_node);
/* Special: if there are zero-driver nodes. We skip here */
if (0 == driver_rr_nodes.size()) {
return;
}
}
if ( (0 == driver_rr_nodes.size())
|| (0 == driver_rr_nodes.size()) ) {
/* No bitstream generation required by a special direct connection*/
return;
} else if (1 < driver_rr_nodes.size()) {
/* Create the block denoting the memory instances that drives this node in Switch Block */
std::string mem_block_name = generate_sb_memory_instance_name(SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string(""));
ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name);
bitstream_manager.add_child_block(sb_configurable_block, mux_mem_block);
/* This is a routing multiplexer! Generate bitstream */
build_switch_block_mux_bitstream(bitstream_manager, mux_mem_block, module_manager,
circuit_lib, mux_lib, rr_graph,
cur_rr_node, driver_rr_nodes,
device_annotation, routing_annotation);
} /*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 ConfigBlockId& sb_config_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGraph& rr_graph,
const RRGSB& rr_gsb) {
/* Iterate over all the multiplexers */
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
SideManager side_manager(side);
for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) {
VTR_ASSERT( (CHANX == rr_graph.node_type(rr_gsb.get_chan_node(side_manager.get_side(), itrack)))
|| (CHANY == rr_graph.node_type(rr_gsb.get_chan_node(side_manager.get_side(), itrack))) );
/* Only output port indicates a routing multiplexer */
if (OUT_PORT != rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) {
continue;
}
build_switch_block_interc_bitstream(bitstream_manager, sb_config_block,
module_manager,
circuit_lib, mux_lib, rr_graph,
device_annotation, routing_annotation,
rr_gsb, side_manager.get_side(), itrack);
}
}
}
/********************************************************************
* This function generates bitstream for a routing multiplexer
* in a Connection 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_connection_block_mux_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& mux_mem_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGraph& rr_graph,
const RRNodeId& src_rr_node) {
/* Find drive_rr_nodes*/
size_t datapath_mux_size = rr_graph.node_fan_in(src_rr_node);
/* Configuration bits for MUX*/
int path_id = DEFAULT_PATH_ID;
int edge_index = 0;
for (const RREdgeId& edge : rr_graph.node_in_edges(src_rr_node)) {
RRNodeId driver_node = rr_graph.edge_src_node(edge);
if (routing_annotation.rr_node_net(driver_node) == routing_annotation.rr_node_net(src_rr_node)) {
path_id = edge_index;
break;
}
edge_index++;
}
/* 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) )
);
/* Find the circuit model id of the mux, we need its design technology which matters the bitstream generation */
std::vector<RRSwitchId> driver_switches = get_rr_graph_driver_switches(rr_graph, src_rr_node);
VTR_ASSERT(1 == driver_switches.size());
CircuitModelId mux_model = device_annotation.rr_switch_circuit_model(driver_switches[0]);
/* 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);
/* 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);
}
}
/********************************************************************
* This function generates bitstream for an interconnection,
* i.e., a routing multiplexer, in a Connection 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_connection_block_interc_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& cb_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGraph& rr_graph,
const RRGSB& rr_gsb,
const e_side& cb_ipin_side,
const size_t& ipin_index) {
RRNodeId src_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, ipin_index);
/* Consider configurable edges only */
std::vector<RRNodeId> driver_rr_nodes = get_rr_graph_configurable_driver_nodes(rr_graph, src_rr_node);
if (1 == driver_rr_nodes.size()) {
/* No bitstream generation required by a special direct connection*/
} else if (1 < driver_rr_nodes.size()) {
/* Create the block denoting the memory instances that drives this node in Switch Block */
std::string mem_block_name = generate_cb_memory_instance_name(CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, rr_graph.node_side(src_rr_node), ipin_index, std::string(""));
ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name);
bitstream_manager.add_child_block(cb_configurable_block, mux_mem_block);
/* This is a routing multiplexer! Generate bitstream */
build_connection_block_mux_bitstream(bitstream_manager, mux_mem_block,
module_manager, circuit_lib, mux_lib,
device_annotation, routing_annotation,
rr_graph, src_rr_node);
} /*Nothing should be done else*/
}
/********************************************************************
* This function generates bitstream for a Connection Block
* and add it to the bitstream manager
* This function will spot all the routing multiplexers in a Connection 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 are the IPIN rr node in a Connection Block
* So, we will iterate over that.
*******************************************************************/
static
void build_connection_block_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& cb_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGraph& rr_graph,
const RRGSB& rr_gsb,
const t_rr_type& cb_type) {
/* Find routing multiplexers on the sides of a Connection block where IPIN nodes locate */
std::vector<enum e_side> cb_sides = rr_gsb.get_cb_ipin_sides(cb_type);
for (size_t side = 0; side < cb_sides.size(); ++side) {
enum e_side cb_ipin_side = cb_sides[side];
SideManager side_manager(cb_ipin_side);
for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) {
build_connection_block_interc_bitstream(bitstream_manager, cb_configurable_block,
module_manager, circuit_lib, mux_lib,
device_annotation, routing_annotation,
rr_graph, rr_gsb,
cb_ipin_side, inode);
}
}
}
/********************************************************************
* Create bitstream for a X-direction or Y-direction Connection Blocks
*******************************************************************/
static
void build_connection_block_bitstreams(BitstreamManager& bitstream_manager,
const ConfigBlockId& top_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGraph& rr_graph,
const DeviceRRGSB& device_rr_gsb,
const t_rr_type& cb_type) {
vtr::Point<size_t> cb_range = device_rr_gsb.get_gsb_range();
for (size_t ix = 0; ix < cb_range.x(); ++ix) {
for (size_t iy = 0; iy < cb_range.y(); ++iy) {
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
/* Check if the connection block exists in the device!
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
* We will skip those modules
*/
if (false == rr_gsb.is_cb_exist(cb_type)) {
continue;
}
/* Skip if the cb does not contain any configuration bits! */
if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) {
continue;
}
/* Create a block for the bitstream which corresponds to the Switch block */
vtr::Point<size_t> cb_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
ConfigBlockId cb_configurable_block = bitstream_manager.add_block(generate_connection_block_module_name(cb_type, cb_coord));
/* Set switch block as a child of top block */
bitstream_manager.add_child_block(top_configurable_block, cb_configurable_block);
build_connection_block_bitstream(bitstream_manager, cb_configurable_block, module_manager,
circuit_lib, mux_lib,
device_annotation, routing_annotation,
rr_graph,
rr_gsb, cb_type);
}
}
}
/********************************************************************
* 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 ConfigBlockId& top_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGraph& rr_graph,
const DeviceRRGSB& 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
*/
VTR_LOG("Generating bitstream for Switch blocks...");
vtr::Point<size_t> sb_range = device_rr_gsb.get_gsb_range();
for (size_t ix = 0; ix < sb_range.x(); ++ix) {
for (size_t iy = 0; iy < sb_range.y(); ++iy) {
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
/* Check if the switch block exists in the device!
* Some of them do NOT exist due to heterogeneous blocks (width > 1)
* We will skip those modules
*/
if (false == rr_gsb.is_sb_exist()) {
continue;
}
/* Create a block for the bitstream which corresponds to the Switch block */
vtr::Point<size_t> sb_coord(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
ConfigBlockId sb_configurable_block = bitstream_manager.add_block(generate_switch_block_module_name(sb_coord));
/* Set switch block as a child of top block */
bitstream_manager.add_child_block(top_configurable_block, sb_configurable_block);
build_switch_block_bitstream(bitstream_manager, sb_configurable_block, module_manager,
circuit_lib, mux_lib,
device_annotation, routing_annotation,
rr_graph,
rr_gsb);
}
}
VTR_LOG("Done\n");
/* 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
*/
VTR_LOG("Generating bitstream for X-direction Connection blocks ...");
build_connection_block_bitstreams(bitstream_manager, top_configurable_block, module_manager,
circuit_lib, mux_lib,
device_annotation, routing_annotation,
rr_graph,
device_rr_gsb, CHANX);
VTR_LOG("Done\n");
VTR_LOG("Generating bitstream for Y-direction Connection blocks ...");
build_connection_block_bitstreams(bitstream_manager, top_configurable_block, module_manager,
circuit_lib, mux_lib,
device_annotation, routing_annotation,
rr_graph,
device_rr_gsb, CHANY);
VTR_LOG("Done");
}
} /* end namespace openfpga */

View File

@ -0,0 +1,39 @@
/********************************************************************
* Header file for build_routing_bitstream.cpp
*******************************************************************/
#ifndef BUILD_ROUTING_BITSTREAM_H
#define BUILD_ROUTING_BITSTREAM_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <vector>
#include "bitstream_manager.h"
#include "vpr_context.h"
#include "module_manager.h"
#include "circuit_library.h"
#include "mux_library.h"
#include "device_rr_gsb.h"
#include "vpr_device_annotation.h"
#include "vpr_routing_annotation.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void build_routing_bitstream(BitstreamManager& bitstream_manager,
const ConfigBlockId& top_configurable_block,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const VprDeviceAnnotation& device_annotation,
const VprRoutingAnnotation& routing_annotation,
const RRGraph& rr_graph,
const DeviceRRGSB& device_rr_gsb);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,15 @@
#ifndef MUX_BITSTREAM_CONSTANTS_H
#define MUX_BITSTREAM_CONSTANTS_H
/* begin namespace openfpga */
namespace openfpga {
/* Default path ID of a unused multiplexer */
#define DEFAULT_PATH_ID -1
/* Default path ID of a unused multiplexer when there are no constant inputs*/
#define DEFAULT_MUX_PATH_ID 0
} /* end namespace openfpga */
#endif