diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 745385cdf..6b2a36d72 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -16,6 +16,7 @@ /* Headers from openfpgashell library */ #include "build_module_graph_utils.h" #include "build_top_module.h" +#include "build_top_module_child_instance.h" #include "build_top_module_connection.h" #include "build_top_module_directs.h" #include "build_top_module_memory.h" @@ -32,637 +33,6 @@ /* begin namespace openfpga */ namespace openfpga { -/******************************************************************** - * Add a instance of a grid module to the top module - *******************************************************************/ -static size_t add_top_module_grid_instance( - ModuleManager& module_manager, const ModuleId& top_module, - t_physical_tile_type_ptr grid_type, const e_side& border_side, - const vtr::Point& grid_coord) { - /* Find the module name for this type of grid */ - 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), - is_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)); - /* Record the instance id */ - size_t grid_instance = module_manager.num_instance(top_module, grid_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, grid_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - std::string instance_name = generate_grid_block_instance_name( - grid_module_name_prefix, std::string(grid_type->name), - is_io_type(grid_type), border_side, grid_coord); - module_manager.set_child_instance_name(top_module, grid_module, grid_instance, - instance_name); - - return grid_instance; -} - -/******************************************************************** - * Add all the grids as sub-modules across the fabric - * The grid modules are created for each unique type of grid (based - * on the type in data structure data_structure - * Here, we will iterate over the full fabric (coordinates) - * and instanciate the grid modules - * - * Return an 2-D array of instance ids of the grid modules that - * have been added - * - * This function assumes an island-style floorplanning for FPGA fabric - * - * - * +-----------------------------------+ - * | I/O grids | - * | TOP side | - * +-----------------------------------+ - * - * +-----------+ +-----------------------------------+ +------------+ - * | | | | | | - * | I/O grids | | Core grids | | I/O grids | - * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | - * | | | | | | - * +-----------+ +-----------------------------------+ +------------+ - * - * +-----------------------------------+ - * | I/O grids | - * | BOTTOM side | - * +-----------------------------------+ - * - *******************************************************************/ -static vtr::Matrix add_top_module_grid_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids) { - vtr::ScopedStartFinishTimer timer("Add grid instances to top module"); - - /* Reserve an array for the instance ids */ - vtr::Matrix grid_instance_ids({grids.width(), grids.height()}); - grid_instance_ids.fill(size_t(-1)); - - /* Instanciate I/O grids */ - /* Create the coordinate range for each side of FPGA fabric */ - std::map>> io_coordinates = - generate_perimeter_grid_coordinates(grids); - - for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { - for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); - /* Bypass EMPTY grid */ - if (true == is_empty_type(phy_tile_type)) { - continue; - } - /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || - (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { - /* Find the root of this grid, the instance id should be valid. - * We just copy it here - */ - vtr::Point root_grid_coord( - io_coordinate.x() - - grids.get_width_offset(io_coordinate.x(), io_coordinate.y()), - io_coordinate.y() - - grids.get_height_offset(io_coordinate.x(), io_coordinate.y())); - VTR_ASSERT(size_t(-1) != - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); - grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; - continue; - } - - /* Add a grid module to top_module*/ - grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = - add_top_module_grid_instance(module_manager, top_module, phy_tile_type, - io_side, io_coordinate); - } - } - - /* Instanciate core grids - * IMPORTANT: sequence matters here, it impacts the I/O indexing. - * We should follow the same sequence as the build_io_location_map()! - * If you change the sequence of walking through grids here, you should change - * it in the build_io_location map()! - */ - for (size_t ix = 1; ix < grids.width() - 1; ++ix) { - for (size_t iy = 1; iy < grids.height() - 1; ++iy) { - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); - /* Bypass EMPTY grid */ - if (true == is_empty_type(phy_tile_type)) { - continue; - } - /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { - /* Find the root of this grid, the instance id should be valid. - * We just copy it here - */ - vtr::Point root_grid_coord( - ix - grids.get_width_offset(ix, iy), - iy - grids.get_height_offset(ix, iy)); - VTR_ASSERT(size_t(-1) != - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); - grid_instance_ids[ix][iy] = - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; - continue; - } - /* Add a grid module to top_module*/ - vtr::Point grid_coord(ix, iy); - grid_instance_ids[ix][iy] = add_top_module_grid_instance( - module_manager, top_module, phy_tile_type, NUM_SIDES, grid_coord); - } - } - - return grid_instance_ids; -} - -/******************************************************************** - * Add switch blocks across the FPGA fabric to the top-level module - * Return an 2-D array of instance ids of the switch blocks that - * have been added - *******************************************************************/ -static vtr::Matrix add_top_module_switch_block_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy) { - vtr::ScopedStartFinishTimer timer("Add switch block instances to top module"); - - vtr::Point sb_range = device_rr_gsb.get_gsb_range(); - - /* Reserve an array for the instance ids */ - vtr::Matrix sb_instance_ids({sb_range.x(), sb_range.y()}); - sb_instance_ids.fill(size_t(-1)); - - for (size_t ix = 0; ix < sb_range.x(); ++ix) { - for (size_t iy = 0; iy < sb_range.y(); ++iy) { - /* If we use compact routing hierarchy, we should instanciate the unique - * module of SB */ - const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - - if (false == rr_gsb.is_sb_exist()) { - continue; - } - - vtr::Point sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); - if (true == compact_routing_hierarchy) { - vtr::Point sb_coord(ix, iy); - const RRGSB& unique_mirror = - device_rr_gsb.get_sb_unique_module(sb_coord); - sb_coordinate.set_x(unique_mirror.get_sb_x()); - sb_coordinate.set_y(unique_mirror.get_sb_y()); - } - std::string sb_module_name = - generate_switch_block_module_name(sb_coordinate); - ModuleId sb_module = module_manager.find_module(sb_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); - /* Record the instance id */ - sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()] = - module_manager.num_instance(top_module, sb_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, sb_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - module_manager.set_child_instance_name( - top_module, sb_module, - sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], - generate_switch_block_module_name( - vtr::Point(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()))); - } - } - - return sb_instance_ids; -} - -/******************************************************************** - * Add switch blocks across the FPGA fabric to the top-level module - *******************************************************************/ -static vtr::Matrix add_top_module_connection_block_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceRRGSB& device_rr_gsb, const t_rr_type& cb_type, - const bool& compact_routing_hierarchy) { - vtr::ScopedStartFinishTimer timer( - "Add connection block instances to top module"); - - vtr::Point cb_range = device_rr_gsb.get_gsb_range(); - - /* Reserve an array for the instance ids */ - vtr::Matrix cb_instance_ids({cb_range.x(), cb_range.y()}); - cb_instance_ids.fill(size_t(-1)); - - for (size_t ix = 0; ix < cb_range.x(); ++ix) { - for (size_t iy = 0; iy < cb_range.y(); ++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 - */ - const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - vtr::Point cb_coordinate(rr_gsb.get_cb_x(cb_type), - rr_gsb.get_cb_y(cb_type)); - if (false == rr_gsb.is_cb_exist(cb_type)) { - continue; - } - /* If we use compact routing hierarchy, we should instanciate the unique - * module of SB */ - if (true == compact_routing_hierarchy) { - vtr::Point cb_coord(ix, iy); - /* Note: use GSB coordinate when inquire for unique modules!!! */ - const RRGSB& unique_mirror = - device_rr_gsb.get_cb_unique_module(cb_type, cb_coord); - cb_coordinate.set_x(unique_mirror.get_cb_x(cb_type)); - cb_coordinate.set_y(unique_mirror.get_cb_y(cb_type)); - } - std::string cb_module_name = - generate_connection_block_module_name(cb_type, cb_coordinate); - ModuleId cb_module = module_manager.find_module(cb_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); - /* Record the instance id */ - cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)] = - module_manager.num_instance(top_module, cb_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, cb_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - std::string cb_instance_name = generate_connection_block_module_name( - cb_type, - vtr::Point(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type))); - module_manager.set_child_instance_name( - top_module, cb_module, - cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)], - cb_instance_name); - } - } - - return cb_instance_ids; -} - -/******************************************************************** - * Add the I/O children to the top-level module, which impacts the I/O indexing - * This is the default function to build the I/O sequence/indexing - * The I/O children is added in a maze shape - * The function supports I/Os in the center of grids, starting from the - *bottom-left corner and ending at the center - * - * +----------------------+ - * |+--------------------+| - * ||+------------------+|| - * |||+----------------+||| - * ||||+-------------->|||| - * ||||+---------------+||| - * |||+-----------------+|| - * ||+-------------------+| - * |+---------------------+ - * ^ - * io[0] - *******************************************************************/ -static void add_top_module_io_children( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids, const vtr::Matrix& grid_instance_ids) { - /* Create the coordinate range for the perimeter I/Os of FPGA fabric */ - std::map>> io_coordinates = - generate_perimeter_grid_coordinates(grids); - - for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { - for (const vtr::Point& io_coord : io_coordinates[io_side]) { - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(io_coord.x(), io_coord.y()); - /* Bypass EMPTY grid */ - if (true == is_empty_type(grid_type)) { - continue; - } - /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coord.x(), io_coord.y())) || - (0 < grids.get_height_offset(io_coord.x(), io_coord.y()))) { - continue; - } - /* Find the module name for this type of grid */ - 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), - is_io_type(grid_type), io_side); - ModuleId grid_module = module_manager.find_module(grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); - /* Add a I/O children to top_module*/ - module_manager.add_io_child(top_module, grid_module, - grid_instance_ids[io_coord.x()][io_coord.y()], - vtr::Point(io_coord.x(), io_coord.y())); - } - } - - /* Walk through the center grids */ - size_t xmin = 1; - size_t xmax = grids.width() - 2; - size_t ymin = 1; - size_t ymax = grids.height() - 2; - std::vector> coords; - while (xmin < xmax && ymin < ymax) { - for (size_t iy = ymin; iy < ymax + 1; iy++) { - coords.push_back(vtr::Point(xmin, iy)); - } - for (size_t ix = xmin + 1; ix < xmax + 1; ix++) { - coords.push_back(vtr::Point(ix, ymax)); - } - for (size_t iy = ymax - 1; iy > ymin; iy--) { - coords.push_back(vtr::Point(xmax, iy)); - } - for (size_t ix = xmax; ix > xmin; ix--) { - coords.push_back(vtr::Point(ix, ymin)); - } - xmin++; - ymin++; - xmax--; - ymax--; - } - - /* If height is odd, add the missing horizental line */ - if ((grids.height() - 2) % 2 == 1) { - if (ymin == ymax) { - for (size_t ix = xmin; ix < xmax + 1; ix++) { - coords.push_back(vtr::Point(ix, ymin)); - } - } - } - /* If width is odd, add the missing vertical line */ - if ((grids.width() - 2) % 2 == 1) { - if (xmin == xmax) { - for (size_t iy = ymin; iy < ymax + 1; iy++) { - coords.push_back(vtr::Point(xmin, iy)); - } - } - } - - /* Now walk through the coordinates */ - for (vtr::Point coord : coords) { - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(coord.x(), coord.y()); - /* Bypass EMPTY grid */ - if (true == is_empty_type(grid_type)) { - continue; - } - /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(coord.x(), coord.y())) || - (0 < grids.get_height_offset(coord.x(), coord.y()))) { - continue; - } - /* Find the module name for this type of grid */ - 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), - is_io_type(grid_type), NUM_SIDES); - ModuleId grid_module = module_manager.find_module(grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); - /* Add a I/O children to top_module*/ - module_manager.add_io_child(top_module, grid_module, - grid_instance_ids[coord.x()][coord.y()], - vtr::Point(coord.x(), coord.y())); - } -} - -/******************************************************************** - * Add the fine-grained instances to the top module of FPGA fabric - * The fine-grained instances include programmable blocks, connection blocks and - *switch blocks, each of which is an instance under the top module - *******************************************************************/ -static int build_top_module_fine_grained_child_instances( - ModuleManager& module_manager, const ModuleId& top_module, - MemoryBankShiftRegisterBanks& blwl_sr_banks, - const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, - const RRClockSpatialLookup& rr_clock_lookup, - const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, - const ArchDirect& arch_direct, const ConfigProtocol& config_protocol, - const CircuitModelId& sram_model, const bool& frame_view, - const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, - const FabricKey& fabric_key) { - int status = CMD_EXEC_SUCCESS; - std::map> cb_instance_ids; - - /* Add sub modules, which are grid, SB and CBX/CBY modules as instances */ - /* Add all the grids across the fabric */ - vtr::Matrix grid_instance_ids = - add_top_module_grid_instances(module_manager, top_module, grids); - /* Add all the SBs across the fabric */ - vtr::Matrix sb_instance_ids = add_top_module_switch_block_instances( - module_manager, top_module, device_rr_gsb, compact_routing_hierarchy); - /* Add all the CBX and CBYs across the fabric */ - cb_instance_ids[CHANX] = add_top_module_connection_block_instances( - module_manager, top_module, device_rr_gsb, CHANX, - compact_routing_hierarchy); - cb_instance_ids[CHANY] = add_top_module_connection_block_instances( - module_manager, top_module, device_rr_gsb, CHANY, - compact_routing_hierarchy); - - /* Update I/O children list */ - add_top_module_io_children(module_manager, top_module, grids, - grid_instance_ids); - - /* Add nets when we need a complete fabric modeling, - * which is required by downstream functions - */ - if (false == frame_view) { - /* Reserve nets to be memory efficient */ - reserve_module_manager_module_nets(module_manager, top_module); - - /* Add module nets to connect the sub modules */ - add_top_module_nets_connect_grids_and_gsbs( - module_manager, top_module, vpr_device_annotation, grids, - grid_instance_ids, rr_graph, device_rr_gsb, sb_instance_ids, - cb_instance_ids, compact_routing_hierarchy, duplicate_grid_pin); - /* Add inter-CLB direct connections */ - add_top_module_nets_tile_direct_connections( - module_manager, top_module, circuit_lib, vpr_device_annotation, grids, - grid_instance_ids, tile_direct, arch_direct); - } - - /* Add global ports from grid ports that are defined as global in tile - * annotation */ - status = add_top_module_global_ports_from_grid_modules( - module_manager, top_module, tile_annotation, vpr_device_annotation, grids, - rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids, clk_ntwk, - rr_clock_lookup); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - - /* Add GPIO ports from the sub-modules under this Verilog module - * For top-level module, we follow a special sequencing for I/O modules. So we - * rebuild the I/O children list here - */ - add_module_gpio_ports_from_child_modules(module_manager, top_module); - - /* Organize the list of memory modules and instances - * If we have an empty fabric key, we organize the memory modules as routine - * Otherwise, we will load the fabric key directly - */ - if (true == fabric_key.empty()) { - organize_top_module_memory_modules( - module_manager, top_module, circuit_lib, config_protocol, sram_model, - grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, - compact_routing_hierarchy); - } else { - VTR_ASSERT_SAFE(false == fabric_key.empty()); - /* Throw a fatal error when the fabric key has a mismatch in region - * organization. between architecture file and fabric key - */ - if (size_t(config_protocol.num_regions()) != fabric_key.regions().size()) { - VTR_LOG_ERROR( - "Fabric key has a different number of configurable regions (='%ld') " - "than architecture definition (=%d)!\n", - fabric_key.regions().size(), config_protocol.num_regions()); - return CMD_EXEC_FATAL_ERROR; - } - - status = load_top_module_memory_modules_from_fabric_key( - module_manager, top_module, circuit_lib, config_protocol, fabric_key); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - - status = load_top_module_shift_register_banks_from_fabric_key( - fabric_key, blwl_sr_banks); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - - /* Update the memory organization in sub module (non-top) */ - status = load_submodules_memory_modules_from_fabric_key( - module_manager, circuit_lib, config_protocol, fabric_key); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Add a instance of a tile module to the top module - *******************************************************************/ -static size_t add_top_module_tile_instance(ModuleManager& module_manager, - const ModuleId& top_module, - const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id) { - /* Find the module name for this type of grid */ - vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); - FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); - vtr::Point unique_tile_coord = - fabric_tile.tile_coordinate(unique_fabric_tile_id); - std::string tile_module_name = generate_tile_module_name(unique_tile_coord); - ModuleId tile_module = module_manager.find_module(tile_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); - /* Record the instance id */ - size_t tile_instance = module_manager.num_instance(top_module, tile_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, tile_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - std::string instance_name = generate_tile_module_name(tile_coord); - module_manager.set_child_instance_name(top_module, tile_module, tile_instance, - instance_name); - return tile_instance; -} - -/******************************************************************** - * Add all the tiles as sub-modules across the fabric - * Here, we will iterate over the full fabric (coordinates) - * and instanciate the tile modules - * - * Return an 2-D array of instance ids of the grid modules that - * have been added - * - * This function assumes an island-style floorplanning for FPGA fabric - * - * - * +-----------------------------------+ - * | I/O tiles | - * | TOP side | - * +-----------------------------------+ - * - * +-----------+ +-----------------------------------+ +------------+ - * | | | | | | - * | I/O tiles | | Core tiles | | I/O tiles | - * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | - * | | | | | | - * +-----------+ +-----------------------------------+ +------------+ - * - * +-----------------------------------+ - * | I/O tiles | - * | BOTTOM side | - * +-----------------------------------+ - * - *******************************************************************/ -static int add_top_module_tile_instances(ModuleManager& module_manager, - const ModuleId& top_module, - vtr::Matrix& tile_instance_ids, - const DeviceGrid& grids, - const FabricTile& fabric_tile) { - vtr::ScopedStartFinishTimer timer("Add tile instances to top module"); - int status = CMD_EXEC_SUCCESS; - - /* Reserve an array for the instance ids */ - tile_instance_ids.resize({grids.width(), grids.height()}); - tile_instance_ids.fill(size_t(-1)); - - /* Instanciate I/O grids */ - /* Create the coordinate range for each side of FPGA fabric */ - std::map>> io_coordinates = - generate_perimeter_tile_coordinates(grids); - - for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { - for (const vtr::Point& io_coord : io_coordinates[io_side]) { - FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); - if (!fabric_tile.valid_tile_id(fabric_tile_id)) { - continue; - } - /* Add a tile module to top_module*/ - tile_instance_ids[io_coord.x()][io_coord.y()] = - add_top_module_tile_instance(module_manager, top_module, fabric_tile, - fabric_tile_id); - } - } - - /* Instanciate core grids - * IMPORTANT: sequence matters here, it impacts the I/O indexing. - * We should follow the same sequence as the build_io_location_map()! - * If you change the sequence of walking through grids here, you should change - * it in the build_io_location map()! - */ - for (size_t ix = 1; ix < grids.width() - 1; ++ix) { - for (size_t iy = 1; iy < grids.height() - 1; ++iy) { - vtr::Point curr_coord(ix, iy); - FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); - if (!fabric_tile.valid_tile_id(fabric_tile_id)) { - continue; - } - /* Add a tile module to top_module*/ - tile_instance_ids[curr_coord.x()][curr_coord.y()] = - add_top_module_tile_instance(module_manager, top_module, fabric_tile, - fabric_tile_id); - } - } - return status; -} - -/******************************************************************** - * Add the tile-level instances to the top module of FPGA fabric - * and build connects between them - *******************************************************************/ -static int build_top_module_tile_child_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids, const FabricTile& fabric_tile) { - int status = CMD_EXEC_SUCCESS; - vtr::Matrix tile_instance_ids; - status = add_top_module_tile_instances(module_manager, top_module, - tile_instance_ids, grids, fabric_tile); - if (status != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; - } - return CMD_EXEC_SUCCESS; -} - /******************************************************************** * Print the top-level module for the FPGA fabric in Verilog format * This function will diff --git a/openfpga/src/fabric/build_top_module_child_instance.cpp b/openfpga/src/fabric/build_top_module_child_instance.cpp new file mode 100644 index 000000000..1b1517e9b --- /dev/null +++ b/openfpga/src/fabric/build_top_module_child_instance.cpp @@ -0,0 +1,666 @@ +/******************************************************************** + * This file includes functions that are used to print the top-level + * module for the FPGA fabric in Verilog format + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +/* Headers from openfpgashell library */ +#include "build_module_graph_utils.h" +#include "build_top_module_child_instance.h" +#include "build_top_module_connection.h" +#include "build_top_module_directs.h" +#include "build_top_module_memory.h" +#include "build_top_module_memory_bank.h" +#include "build_top_module_utils.h" +#include "command_exit_codes.h" +#include "module_manager_memory_utils.h" +#include "module_manager_utils.h" +#include "openfpga_device_grid_utils.h" +#include "openfpga_naming.h" +#include "openfpga_reserved_words.h" +#include "rr_gsb_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Add a instance of a grid module to the top module + *******************************************************************/ +static size_t add_top_module_grid_instance( + ModuleManager& module_manager, const ModuleId& top_module, + t_physical_tile_type_ptr grid_type, const e_side& border_side, + const vtr::Point& grid_coord) { + /* Find the module name for this type of grid */ + 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), + is_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)); + /* Record the instance id */ + size_t grid_instance = module_manager.num_instance(top_module, grid_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, grid_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string instance_name = generate_grid_block_instance_name( + grid_module_name_prefix, std::string(grid_type->name), + is_io_type(grid_type), border_side, grid_coord); + module_manager.set_child_instance_name(top_module, grid_module, grid_instance, + instance_name); + + return grid_instance; +} + +/******************************************************************** + * Add all the grids as sub-modules across the fabric + * The grid modules are created for each unique type of grid (based + * on the type in data structure data_structure + * Here, we will iterate over the full fabric (coordinates) + * and instanciate the grid modules + * + * Return an 2-D array of instance ids of the grid modules that + * have been added + * + * This function assumes an island-style floorplanning for FPGA fabric + * + * + * +-----------------------------------+ + * | I/O grids | + * | TOP side | + * +-----------------------------------+ + * + * +-----------+ +-----------------------------------+ +------------+ + * | | | | | | + * | I/O grids | | Core grids | | I/O grids | + * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | + * | | | | | | + * +-----------+ +-----------------------------------+ +------------+ + * + * +-----------------------------------+ + * | I/O grids | + * | BOTTOM side | + * +-----------------------------------+ + * + *******************************************************************/ +static vtr::Matrix add_top_module_grid_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids) { + vtr::ScopedStartFinishTimer timer("Add grid instances to top module"); + + /* Reserve an array for the instance ids */ + vtr::Matrix grid_instance_ids({grids.width(), grids.height()}); + grid_instance_ids.fill(size_t(-1)); + + /* Instanciate I/O grids */ + /* Create the coordinate range for each side of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_grid_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + /* Bypass EMPTY grid */ + if (true == is_empty_type(phy_tile_type)) { + continue; + } + /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + /* Find the root of this grid, the instance id should be valid. + * We just copy it here + */ + vtr::Point root_grid_coord( + io_coordinate.x() - + grids.get_width_offset(io_coordinate.x(), io_coordinate.y()), + io_coordinate.y() - + grids.get_height_offset(io_coordinate.x(), io_coordinate.y())); + VTR_ASSERT(size_t(-1) != + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); + grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; + continue; + } + + /* Add a grid module to top_module*/ + grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = + add_top_module_grid_instance(module_manager, top_module, phy_tile_type, + io_side, io_coordinate); + } + } + + /* Instanciate core grids + * IMPORTANT: sequence matters here, it impacts the I/O indexing. + * We should follow the same sequence as the build_io_location_map()! + * If you change the sequence of walking through grids here, you should change + * it in the build_io_location map()! + */ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); + /* Bypass EMPTY grid */ + if (true == is_empty_type(phy_tile_type)) { + continue; + } + /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(ix, iy)) || + (0 < grids.get_height_offset(ix, iy))) { + /* Find the root of this grid, the instance id should be valid. + * We just copy it here + */ + vtr::Point root_grid_coord( + ix - grids.get_width_offset(ix, iy), + iy - grids.get_height_offset(ix, iy)); + VTR_ASSERT(size_t(-1) != + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); + grid_instance_ids[ix][iy] = + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; + continue; + } + /* Add a grid module to top_module*/ + vtr::Point grid_coord(ix, iy); + grid_instance_ids[ix][iy] = add_top_module_grid_instance( + module_manager, top_module, phy_tile_type, NUM_SIDES, grid_coord); + } + } + + return grid_instance_ids; +} + +/******************************************************************** + * Add switch blocks across the FPGA fabric to the top-level module + * Return an 2-D array of instance ids of the switch blocks that + * have been added + *******************************************************************/ +static vtr::Matrix add_top_module_switch_block_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy) { + vtr::ScopedStartFinishTimer timer("Add switch block instances to top module"); + + vtr::Point sb_range = device_rr_gsb.get_gsb_range(); + + /* Reserve an array for the instance ids */ + vtr::Matrix sb_instance_ids({sb_range.x(), sb_range.y()}); + sb_instance_ids.fill(size_t(-1)); + + for (size_t ix = 0; ix < sb_range.x(); ++ix) { + for (size_t iy = 0; iy < sb_range.y(); ++iy) { + /* If we use compact routing hierarchy, we should instanciate the unique + * module of SB */ + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + + if (false == rr_gsb.is_sb_exist()) { + continue; + } + + vtr::Point sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + if (true == compact_routing_hierarchy) { + vtr::Point sb_coord(ix, iy); + const RRGSB& unique_mirror = + device_rr_gsb.get_sb_unique_module(sb_coord); + sb_coordinate.set_x(unique_mirror.get_sb_x()); + sb_coordinate.set_y(unique_mirror.get_sb_y()); + } + std::string sb_module_name = + generate_switch_block_module_name(sb_coordinate); + ModuleId sb_module = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + /* Record the instance id */ + sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()] = + module_manager.num_instance(top_module, sb_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, sb_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + module_manager.set_child_instance_name( + top_module, sb_module, + sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], + generate_switch_block_module_name( + vtr::Point(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()))); + } + } + + return sb_instance_ids; +} + +/******************************************************************** + * Add switch blocks across the FPGA fabric to the top-level module + *******************************************************************/ +static vtr::Matrix add_top_module_connection_block_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceRRGSB& device_rr_gsb, const t_rr_type& cb_type, + const bool& compact_routing_hierarchy) { + vtr::ScopedStartFinishTimer timer( + "Add connection block instances to top module"); + + vtr::Point cb_range = device_rr_gsb.get_gsb_range(); + + /* Reserve an array for the instance ids */ + vtr::Matrix cb_instance_ids({cb_range.x(), cb_range.y()}); + cb_instance_ids.fill(size_t(-1)); + + for (size_t ix = 0; ix < cb_range.x(); ++ix) { + for (size_t iy = 0; iy < cb_range.y(); ++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 + */ + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + vtr::Point cb_coordinate(rr_gsb.get_cb_x(cb_type), + rr_gsb.get_cb_y(cb_type)); + if (false == rr_gsb.is_cb_exist(cb_type)) { + continue; + } + /* If we use compact routing hierarchy, we should instanciate the unique + * module of SB */ + if (true == compact_routing_hierarchy) { + vtr::Point cb_coord(ix, iy); + /* Note: use GSB coordinate when inquire for unique modules!!! */ + const RRGSB& unique_mirror = + device_rr_gsb.get_cb_unique_module(cb_type, cb_coord); + cb_coordinate.set_x(unique_mirror.get_cb_x(cb_type)); + cb_coordinate.set_y(unique_mirror.get_cb_y(cb_type)); + } + std::string cb_module_name = + generate_connection_block_module_name(cb_type, cb_coordinate); + ModuleId cb_module = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); + /* Record the instance id */ + cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)] = + module_manager.num_instance(top_module, cb_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, cb_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string cb_instance_name = generate_connection_block_module_name( + cb_type, + vtr::Point(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type))); + module_manager.set_child_instance_name( + top_module, cb_module, + cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)], + cb_instance_name); + } + } + + return cb_instance_ids; +} + +/******************************************************************** + * Add the I/O children to the top-level module, which impacts the I/O indexing + * This is the default function to build the I/O sequence/indexing + * The I/O children is added in a maze shape + * The function supports I/Os in the center of grids, starting from the + *bottom-left corner and ending at the center + * + * +----------------------+ + * |+--------------------+| + * ||+------------------+|| + * |||+----------------+||| + * ||||+-------------->|||| + * ||||+---------------+||| + * |||+-----------------+|| + * ||+-------------------+| + * |+---------------------+ + * ^ + * io[0] + *******************************************************************/ +static void add_top_module_io_children( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const vtr::Matrix& grid_instance_ids) { + /* Create the coordinate range for the perimeter I/Os of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_grid_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coord : io_coordinates[io_side]) { + t_physical_tile_type_ptr grid_type = + grids.get_physical_type(io_coord.x(), io_coord.y()); + /* Bypass EMPTY grid */ + if (true == is_empty_type(grid_type)) { + continue; + } + /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(io_coord.x(), io_coord.y())) || + (0 < grids.get_height_offset(io_coord.x(), io_coord.y()))) { + continue; + } + /* Find the module name for this type of grid */ + 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), + is_io_type(grid_type), io_side); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + /* Add a I/O children to top_module*/ + module_manager.add_io_child(top_module, grid_module, + grid_instance_ids[io_coord.x()][io_coord.y()], + vtr::Point(io_coord.x(), io_coord.y())); + } + } + + /* Walk through the center grids */ + size_t xmin = 1; + size_t xmax = grids.width() - 2; + size_t ymin = 1; + size_t ymax = grids.height() - 2; + std::vector> coords; + while (xmin < xmax && ymin < ymax) { + for (size_t iy = ymin; iy < ymax + 1; iy++) { + coords.push_back(vtr::Point(xmin, iy)); + } + for (size_t ix = xmin + 1; ix < xmax + 1; ix++) { + coords.push_back(vtr::Point(ix, ymax)); + } + for (size_t iy = ymax - 1; iy > ymin; iy--) { + coords.push_back(vtr::Point(xmax, iy)); + } + for (size_t ix = xmax; ix > xmin; ix--) { + coords.push_back(vtr::Point(ix, ymin)); + } + xmin++; + ymin++; + xmax--; + ymax--; + } + + /* If height is odd, add the missing horizental line */ + if ((grids.height() - 2) % 2 == 1) { + if (ymin == ymax) { + for (size_t ix = xmin; ix < xmax + 1; ix++) { + coords.push_back(vtr::Point(ix, ymin)); + } + } + } + /* If width is odd, add the missing vertical line */ + if ((grids.width() - 2) % 2 == 1) { + if (xmin == xmax) { + for (size_t iy = ymin; iy < ymax + 1; iy++) { + coords.push_back(vtr::Point(xmin, iy)); + } + } + } + + /* Now walk through the coordinates */ + for (vtr::Point coord : coords) { + t_physical_tile_type_ptr grid_type = + grids.get_physical_type(coord.x(), coord.y()); + /* Bypass EMPTY grid */ + if (true == is_empty_type(grid_type)) { + continue; + } + /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(coord.x(), coord.y())) || + (0 < grids.get_height_offset(coord.x(), coord.y()))) { + continue; + } + /* Find the module name for this type of grid */ + 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), + is_io_type(grid_type), NUM_SIDES); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + /* Add a I/O children to top_module*/ + module_manager.add_io_child(top_module, grid_module, + grid_instance_ids[coord.x()][coord.y()], + vtr::Point(coord.x(), coord.y())); + } +} + +/******************************************************************** + * Add the fine-grained instances to the top module of FPGA fabric + * The fine-grained instances include programmable blocks, connection blocks and + *switch blocks, each of which is an instance under the top module + *******************************************************************/ +static int build_top_module_fine_grained_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + MemoryBankShiftRegisterBanks& blwl_sr_banks, + const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, + const ArchDirect& arch_direct, const ConfigProtocol& config_protocol, + const CircuitModelId& sram_model, const bool& frame_view, + const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, + const FabricKey& fabric_key) { + int status = CMD_EXEC_SUCCESS; + std::map> cb_instance_ids; + + /* Add sub modules, which are grid, SB and CBX/CBY modules as instances */ + /* Add all the grids across the fabric */ + vtr::Matrix grid_instance_ids = + add_top_module_grid_instances(module_manager, top_module, grids); + /* Add all the SBs across the fabric */ + vtr::Matrix sb_instance_ids = add_top_module_switch_block_instances( + module_manager, top_module, device_rr_gsb, compact_routing_hierarchy); + /* Add all the CBX and CBYs across the fabric */ + cb_instance_ids[CHANX] = add_top_module_connection_block_instances( + module_manager, top_module, device_rr_gsb, CHANX, + compact_routing_hierarchy); + cb_instance_ids[CHANY] = add_top_module_connection_block_instances( + module_manager, top_module, device_rr_gsb, CHANY, + compact_routing_hierarchy); + + /* Update I/O children list */ + add_top_module_io_children(module_manager, top_module, grids, + grid_instance_ids); + + /* Add nets when we need a complete fabric modeling, + * which is required by downstream functions + */ + if (false == frame_view) { + /* Reserve nets to be memory efficient */ + reserve_module_manager_module_nets(module_manager, top_module); + + /* Add module nets to connect the sub modules */ + add_top_module_nets_connect_grids_and_gsbs( + module_manager, top_module, vpr_device_annotation, grids, + grid_instance_ids, rr_graph, device_rr_gsb, sb_instance_ids, + cb_instance_ids, compact_routing_hierarchy, duplicate_grid_pin); + /* Add inter-CLB direct connections */ + add_top_module_nets_tile_direct_connections( + module_manager, top_module, circuit_lib, vpr_device_annotation, grids, + grid_instance_ids, tile_direct, arch_direct); + } + + /* Add global ports from grid ports that are defined as global in tile + * annotation */ + status = add_top_module_global_ports_from_grid_modules( + module_manager, top_module, tile_annotation, vpr_device_annotation, grids, + rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids, clk_ntwk, + rr_clock_lookup); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + /* Add GPIO ports from the sub-modules under this Verilog module + * For top-level module, we follow a special sequencing for I/O modules. So we + * rebuild the I/O children list here + */ + add_module_gpio_ports_from_child_modules(module_manager, top_module); + + /* Organize the list of memory modules and instances + * If we have an empty fabric key, we organize the memory modules as routine + * Otherwise, we will load the fabric key directly + */ + if (true == fabric_key.empty()) { + organize_top_module_memory_modules( + module_manager, top_module, circuit_lib, config_protocol, sram_model, + grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, + compact_routing_hierarchy); + } else { + VTR_ASSERT_SAFE(false == fabric_key.empty()); + /* Throw a fatal error when the fabric key has a mismatch in region + * organization. between architecture file and fabric key + */ + if (size_t(config_protocol.num_regions()) != fabric_key.regions().size()) { + VTR_LOG_ERROR( + "Fabric key has a different number of configurable regions (='%ld') " + "than architecture definition (=%d)!\n", + fabric_key.regions().size(), config_protocol.num_regions()); + return CMD_EXEC_FATAL_ERROR; + } + + status = load_top_module_memory_modules_from_fabric_key( + module_manager, top_module, circuit_lib, config_protocol, fabric_key); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + status = load_top_module_shift_register_banks_from_fabric_key( + fabric_key, blwl_sr_banks); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + /* Update the memory organization in sub module (non-top) */ + status = load_submodules_memory_modules_from_fabric_key( + module_manager, circuit_lib, config_protocol, fabric_key); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Add a instance of a tile module to the top module + *******************************************************************/ +static size_t add_top_module_tile_instance(ModuleManager& module_manager, + const ModuleId& top_module, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id) { + /* Find the module name for this type of grid */ + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); + vtr::Point unique_tile_coord = + fabric_tile.tile_coordinate(unique_fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); + /* Record the instance id */ + size_t tile_instance = module_manager.num_instance(top_module, tile_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, tile_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string instance_name = generate_tile_module_name(tile_coord); + module_manager.set_child_instance_name(top_module, tile_module, tile_instance, + instance_name); + return tile_instance; +} + +/******************************************************************** + * Add all the tiles as sub-modules across the fabric + * Here, we will iterate over the full fabric (coordinates) + * and instanciate the tile modules + * + * Return an 2-D array of instance ids of the grid modules that + * have been added + * + * This function assumes an island-style floorplanning for FPGA fabric + * + * + * +-----------------------------------+ + * | I/O tiles | + * | TOP side | + * +-----------------------------------+ + * + * +-----------+ +-----------------------------------+ +------------+ + * | | | | | | + * | I/O tiles | | Core tiles | | I/O tiles | + * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | + * | | | | | | + * +-----------+ +-----------------------------------+ +------------+ + * + * +-----------------------------------+ + * | I/O tiles | + * | BOTTOM side | + * +-----------------------------------+ + * + *******************************************************************/ +static int add_top_module_tile_instances(ModuleManager& module_manager, + const ModuleId& top_module, + vtr::Matrix& tile_instance_ids, + const DeviceGrid& grids, + const FabricTile& fabric_tile) { + vtr::ScopedStartFinishTimer timer("Add tile instances to top module"); + int status = CMD_EXEC_SUCCESS; + + /* Reserve an array for the instance ids */ + tile_instance_ids.resize({grids.width(), grids.height()}); + tile_instance_ids.fill(size_t(-1)); + + /* Instanciate I/O grids */ + /* Create the coordinate range for each side of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_tile_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coord : io_coordinates[io_side]) { + FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[io_coord.x()][io_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + + /* Instanciate core grids + * IMPORTANT: sequence matters here, it impacts the I/O indexing. + * We should follow the same sequence as the build_io_location_map()! + * If you change the sequence of walking through grids here, you should change + * it in the build_io_location map()! + */ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point curr_coord(ix, iy); + FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[curr_coord.x()][curr_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + return status; +} + +/******************************************************************** + * Add the tile-level instances to the top module of FPGA fabric + * and build connects between them + *******************************************************************/ +int build_top_module_tile_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const FabricTile& fabric_tile) { + int status = CMD_EXEC_SUCCESS; + vtr::Matrix tile_instance_ids; + status = add_top_module_tile_instances(module_manager, top_module, + tile_instance_ids, grids, fabric_tile); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + return CMD_EXEC_SUCCESS; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_child_instance.h b/openfpga/src/fabric/build_top_module_child_instance.h new file mode 100644 index 000000000..bd728d19b --- /dev/null +++ b/openfpga/src/fabric/build_top_module_child_instance.h @@ -0,0 +1,54 @@ +#ifndef BUILD_TOP_MODULE_CHILD_INSTANCE_H +#define BUILD_TOP_MODULE_CHILD_INSTANCE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include + +#include "arch_direct.h" +#include "circuit_library.h" +#include "clock_network.h" +#include "config_protocol.h" +#include "decoder_library.h" +#include "device_grid.h" +#include "device_rr_gsb.h" +#include "fabric_key.h" +#include "fabric_tile.h" +#include "memory_bank_shift_register_banks.h" +#include "module_manager.h" +#include "rr_clock_spatial_lookup.h" +#include "rr_graph_view.h" +#include "tile_annotation.h" +#include "tile_direct.h" +#include "vpr_device_annotation.h" +#include "vtr_geometry.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int build_top_module_fine_grained_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + MemoryBankShiftRegisterBanks& blwl_sr_banks, + const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, + const ArchDirect& arch_direct, const ConfigProtocol& config_protocol, + const CircuitModelId& sram_model, const bool& frame_view, + const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, + const FabricKey& fabric_key); + +static int build_top_module_tile_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const FabricTile& fabric_tile); + +} /* end namespace openfpga */ + +#endif