[Engine] Move most utilized functions for memory bank configuration protocol to a separated source file

This commit is contained in:
tangxifan 2021-09-05 20:45:56 -07:00
parent 475ce2c6d9
commit 1085e468e2
6 changed files with 466 additions and 36 deletions

View File

@ -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<int, int> child_x_range(std::numeric_limits<int>::max(), std::numeric_limits<int>::min()); // Deposit an invalid range first: LSB->max(); MSB->min()
std::pair<int, int> child_y_range(std::numeric_limits<int>::max(), std::numeric_limits<int>::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<int> 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<int, int> child_x_range = compute_memory_bank_regional_configurable_child_x_range(module_manager, top_module, config_region);
std::pair<int, int> child_y_range = compute_memory_bank_regional_configurable_child_y_range(module_manager, top_module, config_region);
std::map<int, size_t> num_bls_per_tile;
std::map<int, size_t> 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<int> 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<int, size_t> num_bls_per_tile = compute_memory_bank_regional_bitline_numbers_per_tile(module_manager, top_module,
config_region,
circuit_lib, sram_model);
std::map<int, size_t> num_wls_per_tile = compute_memory_bank_regional_wordline_numbers_per_tile(module_manager, top_module,
config_region,
circuit_lib, sram_model);
std::map<int, size_t> 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<int, size_t> 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<int, size_t> bl_start_index_per_tile = compute_memory_bank_regional_blwl_start_index_per_tile(child_x_range, num_bls_per_tile);
std::map<int, size_t> 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,

View File

@ -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 */

View File

@ -0,0 +1,237 @@
/********************************************************************
* This file includes functions to build fabric dependent bitstream
* for memory bank configuration protocol
*******************************************************************/
#include <string>
#include <cmath>
#include <algorithm>
/* 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<ModuleId> 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<ModuleId> 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<char> 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<char> 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<ModuleId> 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 */

View File

@ -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 <vector>
#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

View File

@ -0,0 +1,105 @@
/********************************************************************
* This file includes functions that are used to organize memories
* in the top module of FPGA fabric
*******************************************************************/
#include <cmath>
#include <limits>
/* 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<int, int> compute_memory_bank_regional_configurable_child_x_range(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region) {
std::pair<int, int> child_x_range(std::numeric_limits<int>::max(), std::numeric_limits<int>::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<int> 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<int, int> compute_memory_bank_regional_configurable_child_y_range(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region) {
std::pair<int, int> child_y_range(std::numeric_limits<int>::max(), std::numeric_limits<int>::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<int> 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<int, size_t> 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<int, size_t> 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<int> 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<int, size_t> 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<int, size_t> 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<int> 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<int, size_t> compute_memory_bank_regional_blwl_start_index_per_tile(const std::pair<int, int>& child_xy_range,
const std::map<int, size_t>& num_blwls_per_tile) {
std::map<int, size_t> 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 */

View File

@ -0,0 +1,74 @@
#ifndef MEMORY_BANK_UTILS_H
#define MEMORY_BANK_UTILS_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <vector>
#include <map>
#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<int, int> 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<int, int> 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<int, size_t> 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<int, size_t> 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<int, size_t> compute_memory_bank_regional_blwl_start_index_per_tile(const std::pair<int, int>& child_xy_range,
const std::map<int, size_t>& num_blwls_per_tile);
} /* end namespace openfpga */
#endif