From 1085e468e2ea55ea58d150efa6a35879ab7d7223 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 5 Sep 2021 20:45:56 -0700 Subject: [PATCH] [Engine] Move most utilized functions for memory bank configuration protocol to a separated source file --- .../fabric/build_top_module_memory_bank.cpp | 49 +--- .../fpga_bitstream/build_fabric_bitstream.cpp | 8 + .../build_fabric_bitstream_memory_bank.cpp | 237 ++++++++++++++++++ .../build_fabric_bitstream_memory_bank.h | 29 +++ openfpga/src/utils/memory_bank_utils.cpp | 105 ++++++++ openfpga/src/utils/memory_bank_utils.h | 74 ++++++ 6 files changed, 466 insertions(+), 36 deletions(-) create mode 100644 openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp create mode 100644 openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.h create mode 100644 openfpga/src/utils/memory_bank_utils.cpp create mode 100644 openfpga/src/utils/memory_bank_utils.h diff --git a/openfpga/src/fabric/build_top_module_memory_bank.cpp b/openfpga/src/fabric/build_top_module_memory_bank.cpp index 97986f3ed..2df41c5e2 100644 --- a/openfpga/src/fabric/build_top_module_memory_bank.cpp +++ b/openfpga/src/fabric/build_top_module_memory_bank.cpp @@ -23,6 +23,7 @@ #include "memory_utils.h" #include "decoder_library_utils.h" #include "module_manager_utils.h" +#include "memory_bank_utils.h" #include "build_decoder_modules.h" #include "build_top_module_memory_bank.h" @@ -281,42 +282,18 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma * Precompute the BLs and WLs distribution across the FPGA fabric * The distribution is a matrix which contains the starting index of BL/WL for each column or row */ - std::pair child_x_range(std::numeric_limits::max(), std::numeric_limits::min()); // Deposit an invalid range first: LSB->max(); MSB->min() - std::pair child_y_range(std::numeric_limits::max(), std::numeric_limits::min()); // Deposit an invalid range first: LSB->max(); MSB->min() - for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) { - vtr::Point coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id]; - child_x_range.first = std::min(coord.x(), child_x_range.first); - child_x_range.second = std::max(coord.x(), child_x_range.second); - child_y_range.first = std::min(coord.y(), child_y_range.first); - child_y_range.second = std::max(coord.y(), child_y_range.second); - } + std::pair child_x_range = compute_memory_bank_regional_configurable_child_x_range(module_manager, top_module, config_region); + std::pair child_y_range = compute_memory_bank_regional_configurable_child_y_range(module_manager, top_module, config_region); - std::map num_bls_per_tile; - std::map num_wls_per_tile; - for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) { - ModuleId child_module = module_manager.region_configurable_children(top_module, config_region)[child_id]; - vtr::Point coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id]; - num_bls_per_tile[coord.x()] = std::max(num_bls_per_tile[coord.x()], find_memory_decoder_data_size(find_module_num_config_bits(module_manager, child_module, circuit_lib, sram_model, CONFIG_MEM_QL_MEMORY_BANK))); - num_wls_per_tile[coord.y()] = std::max(num_wls_per_tile[coord.y()], find_memory_decoder_data_size(find_module_num_config_bits(module_manager, child_module, circuit_lib, sram_model, CONFIG_MEM_QL_MEMORY_BANK))); - } + std::map num_bls_per_tile = compute_memory_bank_regional_bitline_numbers_per_tile(module_manager, top_module, + config_region, + circuit_lib, sram_model); + std::map num_wls_per_tile = compute_memory_bank_regional_wordline_numbers_per_tile(module_manager, top_module, + config_region, + circuit_lib, sram_model); - std::map bl_starting_index_per_tile; - for (int ibl = child_x_range.first; ibl <= child_x_range.second; ++ibl) { - if (ibl == child_x_range.first) { - bl_starting_index_per_tile[ibl] = 0; - } else { - bl_starting_index_per_tile[ibl] = num_bls_per_tile[ibl - 1] + bl_starting_index_per_tile[ibl - 1]; - } - } - - std::map wl_starting_index_per_tile; - for (int iwl = child_y_range.first; iwl <= child_y_range.second; ++iwl) { - if (iwl == child_y_range.first) { - wl_starting_index_per_tile[iwl] = 0; - } else { - wl_starting_index_per_tile[iwl] = num_wls_per_tile[iwl - 1] + wl_starting_index_per_tile[iwl - 1]; - } - } + std::map bl_start_index_per_tile = compute_memory_bank_regional_blwl_start_index_per_tile(child_x_range, num_bls_per_tile); + std::map wl_start_index_per_tile = compute_memory_bank_regional_blwl_start_index_per_tile(child_y_range, num_wls_per_tile); /************************************************************** * Add nets from BL data out to each configurable child @@ -360,7 +337,7 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma /* Find the BL decoder data index: * It should be the starting index plus an offset which is the residual when divided by the number of BLs in this tile */ - size_t bl_pin_id = bl_starting_index_per_tile[coord.x()] + std::floor(cur_bl_index / child_num_unique_blwls); + size_t bl_pin_id = bl_start_index_per_tile[coord.x()] + std::floor(cur_bl_index / child_num_unique_blwls); if (!(bl_pin_id < bl_decoder_dout_port_info.pins().size())) VTR_ASSERT(bl_pin_id < bl_decoder_dout_port_info.pins().size()); @@ -403,7 +380,7 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma /* Find the WL decoder data index: * It should be the starting index plus an offset which is the residual when divided by the number of WLs in this tile */ - size_t wl_pin_id = wl_starting_index_per_tile[coord.x()] + cur_wl_index % child_num_unique_blwls; + size_t wl_pin_id = wl_start_index_per_tile[coord.x()] + cur_wl_index % child_num_unique_blwls; /* Create net */ ModuleNetId net = create_module_source_pin_net(module_manager, top_module, diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index cf028b084..9065a6ae5 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -19,6 +19,7 @@ #include "decoder_library_utils.h" #include "bitstream_manager_utils.h" #include "build_fabric_bitstream.h" +#include "build_fabric_bitstream_memory_bank.h" /* begin namespace openfpga */ namespace openfpga { @@ -575,6 +576,13 @@ void build_module_fabric_dependent_bitstream(const ConfigProtocol& config_protoc } break; } + case CONFIG_MEM_QL_MEMORY_BANK: { + build_module_fabric_dependent_bitstream_ql_memory_bank(config_protocol, + bitstream_manager, top_block, + module_manager, top_module, + fabric_bitstream); + break; + } case CONFIG_MEM_FRAME_BASED: { /* Find address port size */ diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp new file mode 100644 index 000000000..1fefa79b2 --- /dev/null +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp @@ -0,0 +1,237 @@ +/******************************************************************** + * This file includes functions to build fabric dependent bitstream + * for memory bank configuration protocol + *******************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "openfpga_decode.h" + +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" + +#include "decoder_library_utils.h" +#include "bitstream_manager_utils.h" +#include "build_fabric_bitstream_memory_bank.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function aims to build a bitstream for memory-bank protocol + * It will walk through all the configurable children under a module + * in a recursive way, following a Depth-First Search (DFS) strategy + * For each configuration child, we use its instance name as a key to spot the + * configuration bits in bitstream manager. + * Note that it is guarentee that the instance name in module manager is + * consistent with the block names in bitstream manager + * We use this link to reorganize the bitstream in the sequence of memories as we stored + * in the configurable_children() and configurable_child_instances() of each module of module manager + * + * In such configuration organization, each memory cell has an unique index. + * Using this index, we can infer the address codes for both BL and WL decoders. + * Note that, we must get the number of BLs and WLs before using this function! + *******************************************************************/ +static +void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream(const BitstreamManager& bitstream_manager, + const ConfigBlockId& parent_block, + const ModuleManager& module_manager, + const ModuleId& top_module, + const ModuleId& parent_module, + const ConfigRegionId& config_region, + const size_t& bl_addr_size, + const size_t& wl_addr_size, + const size_t& num_bls, + const size_t& num_wls, + size_t& cur_mem_index, + FabricBitstream& fabric_bitstream, + const FabricBitRegionId& fabric_bitstream_region) { + + /* Depth-first search: if we have any children in the parent_block, + * we dive to the next level first! + */ + if (0 < bitstream_manager.block_children(parent_block).size()) { + /* For top module: + * - Use regional configurable children + * - we will skip the two decoders at the end of the configurable children list + */ + if (parent_module == top_module) { + std::vector configurable_children = module_manager.region_configurable_children(parent_module, config_region); + + VTR_ASSERT(2 <= configurable_children.size()); + size_t num_configurable_children = configurable_children.size() - 2; + + /* Early exit if there is no configurable children */ + if (0 == num_configurable_children) { + /* Ensure that there should be no configuration bits in the parent block */ + VTR_ASSERT(0 == bitstream_manager.block_bits(parent_block).size()); + return; + } + + for (size_t child_id = 0; child_id < num_configurable_children; ++child_id) { + ModuleId child_module = configurable_children[child_id]; + size_t child_instance = module_manager.region_configurable_child_instances(parent_module, config_region)[child_id]; + + /* Get the instance name and ensure it is not empty */ + std::string instance_name = module_manager.instance_name(parent_module, child_module, child_instance); + + /* Find the child block that matches the instance name! */ + ConfigBlockId child_block = bitstream_manager.find_child_block(parent_block, instance_name); + /* We must have one valid block id! */ + VTR_ASSERT(true == bitstream_manager.valid_block_id(child_block)); + + /* Go recursively */ + rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream(bitstream_manager, child_block, + module_manager, top_module, child_module, + config_region, + bl_addr_size, wl_addr_size, + num_bls, num_wls, + cur_mem_index, + fabric_bitstream, + fabric_bitstream_region); + } + } else { + VTR_ASSERT(parent_module != top_module); + /* For other modules: + * - Use configurable children directly + * - no need to exclude decoders as they are not there + */ + std::vector configurable_children = module_manager.configurable_children(parent_module); + + size_t num_configurable_children = configurable_children.size(); + + /* Early exit if there is no configurable children */ + if (0 == num_configurable_children) { + /* Ensure that there should be no configuration bits in the parent block */ + VTR_ASSERT(0 == bitstream_manager.block_bits(parent_block).size()); + return; + } + + for (size_t child_id = 0; child_id < num_configurable_children; ++child_id) { + ModuleId child_module = configurable_children[child_id]; + size_t child_instance = module_manager.configurable_child_instances(parent_module)[child_id]; + + /* Get the instance name and ensure it is not empty */ + std::string instance_name = module_manager.instance_name(parent_module, child_module, child_instance); + + /* Find the child block that matches the instance name! */ + ConfigBlockId child_block = bitstream_manager.find_child_block(parent_block, instance_name); + /* We must have one valid block id! */ + VTR_ASSERT(true == bitstream_manager.valid_block_id(child_block)); + + /* Go recursively */ + rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream(bitstream_manager, child_block, + module_manager, top_module, child_module, + config_region, + bl_addr_size, wl_addr_size, + num_bls, num_wls, + cur_mem_index, + fabric_bitstream, + fabric_bitstream_region); + } + } + /* Ensure that there should be no configuration bits in the parent block */ + VTR_ASSERT(0 == bitstream_manager.block_bits(parent_block).size()); + + return; + } + + /* Note that, reach here, it means that this is a leaf node. + * We add the configuration bits to the fabric_bitstream, + * And then, we can return + */ + for (const ConfigBitId& config_bit : bitstream_manager.block_bits(parent_block)) { + FabricBitId fabric_bit = fabric_bitstream.add_bit(config_bit); + + /* Find BL address */ + size_t cur_bl_index = std::floor(cur_mem_index / num_bls); + std::vector bl_addr_bits_vec = itobin_charvec(cur_bl_index, bl_addr_size); + + /* Find WL address */ + size_t cur_wl_index = cur_mem_index % num_wls; + std::vector wl_addr_bits_vec = itobin_charvec(cur_wl_index, wl_addr_size); + + /* Set BL address */ + fabric_bitstream.set_bit_bl_address(fabric_bit, bl_addr_bits_vec); + + /* Set WL address */ + fabric_bitstream.set_bit_wl_address(fabric_bit, wl_addr_bits_vec); + + /* Set data input */ + fabric_bitstream.set_bit_din(fabric_bit, bitstream_manager.bit_value(config_bit)); + + /* Add the bit to the region */ + fabric_bitstream.add_bit_to_region(fabric_bitstream_region, fabric_bit); + + /* Increase the memory index */ + cur_mem_index++; + } +} + +/******************************************************************** + * Main function to build a fabric-dependent bitstream + * by considering the configuration protocol types + *******************************************************************/ +void build_module_fabric_dependent_bitstream_ql_memory_bank(const ConfigProtocol& config_protocol, + const BitstreamManager& bitstream_manager, + const ConfigBlockId& top_block, + const ModuleManager& module_manager, + const ModuleId& top_module, + FabricBitstream& fabric_bitstream) { + /* Ensure we are in the correct type of configuration protocol*/ + VTR_ASSERT(config_protocol.type() == CONFIG_MEM_QL_MEMORY_BANK); + + /* Find global BL address port size */ + ModulePortId bl_addr_port = module_manager.find_module_port(top_module, std::string(DECODER_BL_ADDRESS_PORT_NAME)); + BasicPort bl_addr_port_info = module_manager.module_port(top_module, bl_addr_port); + + /* Find global WL address port size */ + ModulePortId wl_addr_port = module_manager.find_module_port(top_module, std::string(DECODER_WL_ADDRESS_PORT_NAME)); + BasicPort wl_addr_port_info = module_manager.module_port(top_module, wl_addr_port); + + /* Reserve bits before build-up */ + fabric_bitstream.set_use_address(true); + fabric_bitstream.set_use_wl_address(true); + fabric_bitstream.set_bl_address_length(bl_addr_port_info.get_width()); + fabric_bitstream.set_wl_address_length(wl_addr_port_info.get_width()); + fabric_bitstream.reserve_bits(bitstream_manager.num_bits()); + + /* Build bitstreams by region */ + for (const ConfigRegionId& config_region : module_manager.regions(top_module)) { + size_t cur_mem_index = 0; + + /* Find port information for local BL and WL decoder in this region */ + std::vector configurable_children = module_manager.region_configurable_children(top_module, config_region); + VTR_ASSERT(2 <= configurable_children.size()); + ModuleId bl_decoder_module = configurable_children[configurable_children.size() - 2]; + ModuleId wl_decoder_module = configurable_children[configurable_children.size() - 1]; + + ModulePortId bl_port = module_manager.find_module_port(bl_decoder_module, std::string(DECODER_DATA_OUT_PORT_NAME)); + BasicPort bl_port_info = module_manager.module_port(bl_decoder_module, bl_port); + + ModulePortId wl_port = module_manager.find_module_port(wl_decoder_module, std::string(DECODER_DATA_OUT_PORT_NAME)); + BasicPort wl_port_info = module_manager.module_port(wl_decoder_module, wl_port); + + /* Build the bitstream for all the blocks in this region */ + FabricBitRegionId fabric_bitstream_region = fabric_bitstream.add_region(); + rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream(bitstream_manager, top_block, + module_manager, top_module, top_module, + config_region, + bl_addr_port_info.get_width(), + wl_addr_port_info.get_width(), + bl_port_info.get_width(), + wl_port_info.get_width(), + cur_mem_index, + fabric_bitstream, + fabric_bitstream_region); + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.h b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.h new file mode 100644 index 000000000..41174a2fe --- /dev/null +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.h @@ -0,0 +1,29 @@ +#ifndef BUILD_FABRIC_BITSTREAM_MEMORY_BANK_H +#define BUILD_FABRIC_BITSTREAM_MEMORY_BANK_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "config_protocol.h" +#include "bitstream_manager.h" +#include "fabric_bitstream.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void build_module_fabric_dependent_bitstream_ql_memory_bank(const ConfigProtocol& config_protocol, + const BitstreamManager& bitstream_manager, + const ConfigBlockId& top_block, + const ModuleManager& module_manager, + const ModuleId& top_module, + FabricBitstream& fabric_bitstream); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/utils/memory_bank_utils.cpp b/openfpga/src/utils/memory_bank_utils.cpp new file mode 100644 index 000000000..d43909e4d --- /dev/null +++ b/openfpga/src/utils/memory_bank_utils.cpp @@ -0,0 +1,105 @@ +/******************************************************************** + * This file includes functions that are used to organize memories + * in the top module of FPGA fabric + *******************************************************************/ +#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 "command_exit_codes.h" + +#include "rr_gsb_utils.h" +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" + +#include "memory_utils.h" +#include "decoder_library_utils.h" +#include "module_manager_utils.h" +#include "memory_bank_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +std::pair compute_memory_bank_regional_configurable_child_x_range(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region) { + std::pair child_x_range(std::numeric_limits::max(), std::numeric_limits::min()); // Deposit an invalid range first: LSB->max(); MSB->min() + for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) { + vtr::Point coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id]; + child_x_range.first = std::min(coord.x(), child_x_range.first); + child_x_range.second = std::max(coord.x(), child_x_range.second); + } + + VTR_ASSERT(child_x_range.first <= child_x_range.second); + return child_x_range; +} + +std::pair compute_memory_bank_regional_configurable_child_y_range(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region) { + std::pair child_y_range(std::numeric_limits::max(), std::numeric_limits::min()); // Deposit an invalid range first: LSB->max(); MSB->min() + for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) { + vtr::Point coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id]; + child_y_range.first = std::min(coord.y(), child_y_range.first); + child_y_range.second = std::max(coord.y(), child_y_range.second); + } + + VTR_ASSERT(child_y_range.first <= child_y_range.second); + return child_y_range; +} + +std::map compute_memory_bank_regional_bitline_numbers_per_tile(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model) { + std::map num_bls_per_tile; + + for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) { + ModuleId child_module = module_manager.region_configurable_children(top_module, config_region)[child_id]; + vtr::Point coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id]; + num_bls_per_tile[coord.x()] = std::max(num_bls_per_tile[coord.x()], find_memory_decoder_data_size(find_module_num_config_bits(module_manager, child_module, circuit_lib, sram_model, CONFIG_MEM_QL_MEMORY_BANK))); + } + + return num_bls_per_tile; +} + +std::map compute_memory_bank_regional_wordline_numbers_per_tile(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model) { + std::map num_wls_per_tile; + + for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) { + ModuleId child_module = module_manager.region_configurable_children(top_module, config_region)[child_id]; + vtr::Point coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id]; + num_wls_per_tile[coord.y()] = std::max(num_wls_per_tile[coord.y()], find_memory_decoder_data_size(find_module_num_config_bits(module_manager, child_module, circuit_lib, sram_model, CONFIG_MEM_QL_MEMORY_BANK))); + } + + return num_wls_per_tile; +} + +std::map compute_memory_bank_regional_blwl_start_index_per_tile(const std::pair& child_xy_range, + const std::map& num_blwls_per_tile) { + std::map blwl_start_index_per_tile; + for (int iblwl = child_xy_range.first; iblwl <= child_xy_range.second; ++iblwl) { + if (iblwl == child_xy_range.first) { + blwl_start_index_per_tile[iblwl] = 0; + } else { + blwl_start_index_per_tile[iblwl] = num_blwls_per_tile.at(iblwl - 1) + blwl_start_index_per_tile[iblwl - 1]; + } + } + return blwl_start_index_per_tile; +} + +} /* end namespace openfpga */ + diff --git a/openfpga/src/utils/memory_bank_utils.h b/openfpga/src/utils/memory_bank_utils.h new file mode 100644 index 000000000..fd08c5e3d --- /dev/null +++ b/openfpga/src/utils/memory_bank_utils.h @@ -0,0 +1,74 @@ +#ifndef MEMORY_BANK_UTILS_H +#define MEMORY_BANK_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include +#include +#include "vtr_vector.h" +#include "vtr_ndmatrix.h" +#include "module_manager.h" +#include "circuit_library.h" +#include "decoder_library.h" +#include "build_top_module_memory_utils.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +/** + * @brief Precompute the range of x coordinates of all the configurable children under a specific configuration region + * The lower bound is stored in the first element of the return struct + * The upper bound is stored in the second element of the return struct + */ +std::pair compute_memory_bank_regional_configurable_child_x_range(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region); + +/** + * @brief Precompute the range of y coordinates of all the configurable children under a specific configuration region + * The lower bound is stored in the first element of the return struct + * The upper bound is stored in the second element of the return struct + */ +std::pair compute_memory_bank_regional_configurable_child_y_range(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region); + +/** + * @brief Precompute the number of bit lines required by each tile under a specific configuration region + * @note + * Not every index in the range computed by the compute_memory_bank_regional_configurable_child_x_range() function has a postive number of bit lines + * If an empty entry is found (e.g., std::map::find(x) is empty), it means there are not bit lines required in that tile + */ +std::map compute_memory_bank_regional_bitline_numbers_per_tile(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model); +/** + * @brief Precompute the number of word lines required by each tile under a specific configuration region + * @note + * Not every index in the range computed by the compute_memory_bank_regional_configurable_child_x_range() function has a postive number of word lines + * If an empty entry is found (e.g., std::map::find(y) is empty), it means there are not word lines required in that tile + */ +std::map compute_memory_bank_regional_wordline_numbers_per_tile(const ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model); + +/** + * @brief Precompute the BLs and WLs distribution across the FPGA fabric + * The distribution is a matrix which contains the starting index of BL/WL for each column or row + */ +std::map compute_memory_bank_regional_blwl_start_index_per_tile(const std::pair& child_xy_range, + const std::map& num_blwls_per_tile); + +} /* end namespace openfpga */ + +#endif