395 lines
14 KiB
C++
395 lines
14 KiB
C++
/***************************************************************************************
|
|
* Output instance hierarchy in SDC to file formats
|
|
***************************************************************************************/
|
|
/* Headers from vtrutil library */
|
|
#include "vtr_assert.h"
|
|
#include "vtr_log.h"
|
|
#include "vtr_time.h"
|
|
|
|
/* Headers from openfpgautil library */
|
|
#include "openfpga_digest.h"
|
|
#include "openfpga_naming.h"
|
|
#include "openfpga_physical_tile_utils.h"
|
|
#include "openfpga_reserved_words.h"
|
|
#include "pb_type_utils.h"
|
|
#include "sdc_hierarchy_writer.h"
|
|
#include "sdc_writer_naming.h"
|
|
|
|
/* begin namespace openfpga */
|
|
namespace openfpga {
|
|
|
|
/***************************************************************************************
|
|
* Write the hierarchy of Switch Block module and its instances to a plain text
|
|
*file e.g., <sdc_file_for_the_module> <module_name>/<instance_name>
|
|
* ...
|
|
* This file is mainly used by hierarchical P&R flow
|
|
*
|
|
* Return 0 if successful
|
|
* Return 1 if there are more serious bugs in the architecture
|
|
* Return 2 if fail when creating files
|
|
***************************************************************************************/
|
|
void print_pnr_sdc_routing_sb_hierarchy(const std::string& sdc_dir,
|
|
const ModuleManager& module_manager,
|
|
const ModuleId& top_module,
|
|
const DeviceRRGSB& device_rr_gsb,
|
|
const RRGraphView& rr_graph) {
|
|
std::string fname(sdc_dir + std::string(SDC_SB_HIERARCHY_FILE_NAME));
|
|
|
|
std::string timer_message =
|
|
std::string("Write Switch Block hierarchy to plain-text file '") + fname +
|
|
std::string("'");
|
|
|
|
std::string dir_path = format_dir_path(find_path_dir_name(fname));
|
|
|
|
/* Create directories */
|
|
create_directory(dir_path);
|
|
|
|
/* Start time count */
|
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
|
|
|
/* Use default name if user does not provide one */
|
|
VTR_ASSERT(true != fname.empty());
|
|
|
|
/* Create a file handler*/
|
|
std::fstream fp;
|
|
/* Open a file */
|
|
fp.open(fname, std::fstream::out | std::fstream::trunc);
|
|
|
|
/* Validate the file stream */
|
|
check_file_stream(fname.c_str(), fp);
|
|
|
|
for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) {
|
|
const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(isb);
|
|
if (false == rr_gsb.is_sb_exist(rr_graph)) {
|
|
continue;
|
|
}
|
|
|
|
/* Find all the sb instance under this module
|
|
* Create a regular expression to include these instance names
|
|
*/
|
|
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
|
std::string sb_module_name =
|
|
generate_switch_block_module_name(gsb_coordinate);
|
|
|
|
ModuleId sb_module = module_manager.find_module(sb_module_name);
|
|
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
|
|
|
/* Create the file name for SDC */
|
|
std::string sdc_fname(sdc_dir +
|
|
generate_switch_block_module_name(gsb_coordinate) +
|
|
std::string(SDC_FILE_NAME_POSTFIX));
|
|
|
|
fp << "- " << sb_module_name << ":"
|
|
<< "\n";
|
|
|
|
/* Go through all the instance */
|
|
for (const size_t& instance_id :
|
|
module_manager.child_module_instances(top_module, sb_module)) {
|
|
std::string sb_instance_name =
|
|
module_manager.instance_name(top_module, sb_module, instance_id);
|
|
fp << " ";
|
|
fp << "- " << sb_instance_name << "\n";
|
|
}
|
|
|
|
fp << "\n";
|
|
}
|
|
|
|
/* close a file */
|
|
fp.close();
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* Write the hierarchy of Switch Block module and its instances to a plain text
|
|
*file e.g., <sdc_file_for_the_module> <module_name>/<instance_name>
|
|
* ...
|
|
* This file is mainly used by hierarchical P&R flow
|
|
*
|
|
* Return 0 if successful
|
|
* Return 1 if there are more serious bugs in the architecture
|
|
* Return 2 if fail when creating files
|
|
***************************************************************************************/
|
|
void print_pnr_sdc_routing_cb_hierarchy(const std::string& sdc_dir,
|
|
const ModuleManager& module_manager,
|
|
const ModuleId& top_module,
|
|
const t_rr_type& cb_type,
|
|
const DeviceRRGSB& device_rr_gsb) {
|
|
std::string fname(sdc_dir);
|
|
if (CHANX == cb_type) {
|
|
fname += std::string(SDC_CBX_HIERARCHY_FILE_NAME);
|
|
} else {
|
|
VTR_ASSERT(CHANY == cb_type);
|
|
fname += std::string(SDC_CBY_HIERARCHY_FILE_NAME);
|
|
}
|
|
|
|
std::string timer_message =
|
|
std::string("Write Connection Block hierarchy to plain-text file '") +
|
|
fname + std::string("'");
|
|
|
|
std::string dir_path = format_dir_path(find_path_dir_name(fname));
|
|
|
|
/* Create directories */
|
|
create_directory(dir_path);
|
|
|
|
/* Start time count */
|
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
|
|
|
/* Use default name if user does not provide one */
|
|
VTR_ASSERT(true != fname.empty());
|
|
|
|
/* Create a file handler*/
|
|
std::fstream fp;
|
|
/* Open a file */
|
|
fp.open(fname, std::fstream::out | std::fstream::trunc);
|
|
|
|
/* Validate the file stream */
|
|
check_file_stream(fname.c_str(), fp);
|
|
|
|
/* Print SDC for unique X-direction connection block modules */
|
|
for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(cb_type);
|
|
++icb) {
|
|
const RRGSB& unique_mirror =
|
|
device_rr_gsb.get_cb_unique_module(cb_type, icb);
|
|
|
|
/* Find all the cb instance under this module
|
|
* Create a regular expression to include these instance names
|
|
*/
|
|
vtr::Point<size_t> gsb_coordinate(unique_mirror.get_cb_x(cb_type),
|
|
unique_mirror.get_cb_y(cb_type));
|
|
std::string cb_module_name =
|
|
generate_connection_block_module_name(cb_type, gsb_coordinate);
|
|
ModuleId cb_module = module_manager.find_module(cb_module_name);
|
|
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
|
|
|
|
/* Create the file name for SDC */
|
|
std::string sdc_fname(
|
|
sdc_dir + generate_connection_block_module_name(cb_type, gsb_coordinate) +
|
|
std::string(SDC_FILE_NAME_POSTFIX));
|
|
|
|
fp << "- " << cb_module_name << ":"
|
|
<< "\n";
|
|
|
|
/* Go through all the instance */
|
|
for (const size_t& instance_id :
|
|
module_manager.child_module_instances(top_module, cb_module)) {
|
|
std::string cb_instance_name =
|
|
module_manager.instance_name(top_module, cb_module, instance_id);
|
|
fp << " ";
|
|
fp << "- " << cb_instance_name << "\n";
|
|
}
|
|
|
|
fp << "\n";
|
|
}
|
|
|
|
/* close a file */
|
|
fp.close();
|
|
}
|
|
|
|
/********************************************************************
|
|
* Recursively write the hierarchy of pb_type and its instances to a plain text
|
|
*file e.g., <sdc_file_for_the_module> <module_name>/<instance_name>
|
|
* ...
|
|
* This file is mainly used by hierarchical P&R flow
|
|
*******************************************************************/
|
|
static void rec_print_pnr_sdc_grid_pb_graph_hierarchy(
|
|
std::fstream& fp, const size_t& depth, const ModuleManager& module_manager,
|
|
const ModuleId& parent_pb_module,
|
|
const VprDeviceAnnotation& device_annotation,
|
|
t_pb_graph_node* parent_pb_graph_node) {
|
|
/* Validate the file stream */
|
|
VTR_ASSERT(true == valid_file_stream(fp));
|
|
|
|
/* Validate pb_graph node */
|
|
if (nullptr == parent_pb_graph_node) {
|
|
VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid parent_pb_graph_node.\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Get the pb_type */
|
|
t_pb_type* parent_pb_type = parent_pb_graph_node->pb_type;
|
|
|
|
std::string pb_module_name =
|
|
generate_physical_block_module_name(parent_pb_type);
|
|
|
|
/* Find the pb module in module manager */
|
|
ModuleId pb_module = module_manager.find_module(pb_module_name);
|
|
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));
|
|
|
|
write_space_to_file(fp, depth * 2);
|
|
fp << "- " << pb_module_name << ":"
|
|
<< "\n";
|
|
|
|
/* Go through all the instance */
|
|
for (const size_t& instance_id :
|
|
module_manager.child_module_instances(parent_pb_module, pb_module)) {
|
|
std::string child_instance_name =
|
|
module_manager.instance_name(parent_pb_module, pb_module, instance_id);
|
|
write_space_to_file(fp, depth * 2);
|
|
fp << " ";
|
|
fp << "- " << child_instance_name;
|
|
|
|
if (true == is_primitive_pb_type(parent_pb_type)) {
|
|
fp << "\n";
|
|
return;
|
|
}
|
|
|
|
fp << ":"
|
|
<< "\n";
|
|
|
|
/* Note we only go through the graph through the physical modes.
|
|
* which we build the modules
|
|
*/
|
|
t_mode* physical_mode = device_annotation.physical_mode(parent_pb_type);
|
|
|
|
/* Go recursively to the lower level in the pb_graph
|
|
* Note that we assume a full hierarchical P&R, we will only visit
|
|
* pb_graph_node of unique pb_type
|
|
*/
|
|
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) {
|
|
rec_print_pnr_sdc_grid_pb_graph_hierarchy(
|
|
fp, depth + 2, module_manager, pb_module, device_annotation,
|
|
&(parent_pb_graph_node
|
|
->child_pb_graph_nodes[physical_mode->index][ipb][jpb]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* Write the hierarchy of grid module and its instances to a plain text file
|
|
* e.g.,
|
|
* <sdc_file_for_the_module>
|
|
* <module_name>/<instance_name>
|
|
* ...
|
|
* This file is mainly used by hierarchical P&R flow
|
|
*
|
|
* Return 0 if successful
|
|
* Return 1 if there are more serious bugs in the architecture
|
|
* Return 2 if fail when creating files
|
|
***************************************************************************************/
|
|
void print_pnr_sdc_grid_hierarchy(const std::string& sdc_dir,
|
|
const DeviceContext& device_ctx,
|
|
const VprDeviceAnnotation& device_annotation,
|
|
const ModuleManager& module_manager,
|
|
const ModuleId& top_module) {
|
|
std::string fname(sdc_dir + std::string(SDC_GRID_HIERARCHY_FILE_NAME));
|
|
|
|
std::string timer_message =
|
|
std::string("Write Grid hierarchy to plain-text file '") + fname +
|
|
std::string("'");
|
|
|
|
std::string dir_path = format_dir_path(find_path_dir_name(fname));
|
|
|
|
/* Create directories */
|
|
create_directory(dir_path);
|
|
|
|
/* Start time count */
|
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
|
|
|
/* Use default name if user does not provide one */
|
|
VTR_ASSERT(true != fname.empty());
|
|
|
|
/* Create a file handler*/
|
|
std::fstream fp;
|
|
/* Open a file */
|
|
fp.open(fname, std::fstream::out | std::fstream::trunc);
|
|
|
|
/* Validate the file stream */
|
|
check_file_stream(fname.c_str(), fp);
|
|
|
|
std::string root_path =
|
|
format_dir_path(module_manager.module_name(top_module));
|
|
|
|
for (const t_physical_tile_type& physical_tile :
|
|
device_ctx.physical_tile_types) {
|
|
/* Bypass empty type or nullptr */
|
|
if (true == is_empty_type(&physical_tile)) {
|
|
continue;
|
|
}
|
|
|
|
for (const t_sub_tile& sub_tile : physical_tile.sub_tiles) {
|
|
VTR_ASSERT(1 == sub_tile.equivalent_sites.size());
|
|
t_pb_graph_node* pb_graph_head =
|
|
sub_tile.equivalent_sites[0]->pb_graph_head;
|
|
if (nullptr == pb_graph_head) {
|
|
continue;
|
|
}
|
|
|
|
if (true == is_io_type(&physical_tile)) {
|
|
/* Special for I/O block:
|
|
* We will search the grids and see where the I/O blocks are located:
|
|
* - If a I/O block locates on border sides of FPGA fabric:
|
|
* i.e., one or more from {TOP, RIGHT, BOTTOM, LEFT},
|
|
* we will generate one module for each border side
|
|
* - If a I/O block locates in the center of FPGA fabric:
|
|
* we will generate one module with NUM_2D_SIDES (same treatment as
|
|
* regular grids)
|
|
*/
|
|
std::set<e_side> io_type_sides =
|
|
find_physical_io_tile_located_sides(device_ctx.grid, &physical_tile);
|
|
|
|
/* Generate the grid module name */
|
|
for (const e_side& io_type_side : io_type_sides) {
|
|
std::string grid_module_name = generate_grid_block_module_name(
|
|
std::string(GRID_MODULE_NAME_PREFIX),
|
|
std::string(physical_tile.name), is_io_type(&physical_tile),
|
|
io_type_side);
|
|
/* Find the module Id */
|
|
ModuleId grid_module = module_manager.find_module(grid_module_name);
|
|
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
|
|
|
|
fp << "- " << grid_module_name << ":"
|
|
<< "\n";
|
|
|
|
/* Go through all the instance */
|
|
for (const size_t& instance_id :
|
|
module_manager.child_module_instances(top_module, grid_module)) {
|
|
std::string grid_instance_name = module_manager.instance_name(
|
|
top_module, grid_module, instance_id);
|
|
fp << " ";
|
|
fp << "- " << grid_instance_name << ":"
|
|
<< "\n";
|
|
|
|
rec_print_pnr_sdc_grid_pb_graph_hierarchy(
|
|
fp, 2, module_manager, grid_module, device_annotation,
|
|
pb_graph_head);
|
|
}
|
|
fp << "\n";
|
|
}
|
|
} else {
|
|
/* For CLB and heterogenenous blocks */
|
|
std::string grid_module_name = generate_grid_block_module_name(
|
|
std::string(GRID_MODULE_NAME_PREFIX), std::string(physical_tile.name),
|
|
is_io_type(&physical_tile), NUM_2D_SIDES);
|
|
/* Find the module Id */
|
|
ModuleId grid_module = module_manager.find_module(grid_module_name);
|
|
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
|
|
|
|
fp << "- " << grid_module_name << ":"
|
|
<< "\n";
|
|
|
|
/* Go through all the instance */
|
|
for (const size_t& instance_id :
|
|
module_manager.child_module_instances(top_module, grid_module)) {
|
|
std::string grid_instance_name =
|
|
module_manager.instance_name(top_module, grid_module, instance_id);
|
|
fp << " ";
|
|
fp << "- " << grid_instance_name << ":"
|
|
<< "\n";
|
|
|
|
rec_print_pnr_sdc_grid_pb_graph_hierarchy(
|
|
fp, 2, module_manager, grid_module, device_annotation,
|
|
pb_graph_head);
|
|
}
|
|
fp << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
/* close a file */
|
|
fp.close();
|
|
}
|
|
|
|
} /* end namespace openfpga */
|