/******************************************************************** * This file includes functions to build fabric dependent bitstream *******************************************************************/ #include #include #include /* Headers from vtrutil library */ #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_time.h" /* Headers from openfpgautil library */ #include "bitstream_manager_utils.h" #include "build_fabric_bitstream.h" #include "build_fabric_bitstream_memory_bank.h" #include "decoder_library_utils.h" #include "openfpga_decode.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" /* begin namespace openfpga */ namespace openfpga { /******************************************************************** * This function aims to build a bitstream for configuration chain-like 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 *******************************************************************/ static void rec_build_module_fabric_dependent_chain_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, 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()) { if (parent_module == top_module) { for (size_t child_id = 0; child_id < module_manager .region_configurable_children( parent_module, config_region) .size(); ++child_id) { ModuleId child_module = module_manager.region_configurable_children( parent_module, config_region)[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_chain_bitstream( bitstream_manager, child_block, module_manager, top_module, child_module, config_region, fabric_bitstream, fabric_bitstream_region); } } else { for (size_t child_id = 0; child_id < module_manager.configurable_children(parent_module).size(); ++child_id) { ModuleId child_module = module_manager.configurable_children(parent_module)[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_chain_bitstream( bitstream_manager, child_block, module_manager, top_module, child_module, config_region, 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()); } /* 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); fabric_bitstream.add_bit_to_region(fabric_bitstream_region, fabric_bit); } } /******************************************************************** * 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_memory_bank_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_memory_bank_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_memory_bank_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++; } } /******************************************************************** * This function aims to build a bitstream for frame-based configuration *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 guarenteed 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 * * For each configuration bits, we will infer its address based on * - the child index in the configurable children list of current module * - the child index of all the parent modules in their configurable children *list until the top in the hierarchy * * The address will be organized as follows: * ... * The address will be decoded to a binary format * * For each configuration bit, the data_in for the frame-based decoders will be * the same as the configuration bit in bitstream manager. *******************************************************************/ static void rec_build_module_fabric_dependent_frame_bitstream( const BitstreamManager& bitstream_manager, const std::vector& parent_blocks, const ModuleManager& module_manager, const ModuleId& top_module, const ConfigRegionId& config_region, const std::vector& parent_modules, const std::vector& addr_code, const char& bitstream_dont_care_char, FabricBitstream& fabric_bitstream, 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_blocks.back()).size()) { const ConfigBlockId& parent_block = parent_blocks.back(); const ModuleId& parent_module = parent_modules.back(); std::vector configurable_children; std::vector configurable_child_instances; if (top_module == parent_module) { configurable_children = module_manager.region_configurable_children( parent_module, config_region); configurable_child_instances = module_manager.region_configurable_child_instances(parent_module, config_region); } else { VTR_ASSERT(top_module != parent_module); configurable_children = module_manager.configurable_children(parent_module); configurable_child_instances = module_manager.configurable_child_instances(parent_module); } size_t num_configurable_children = configurable_children.size(); size_t max_child_addr_code_size = 0; bool add_addr_code = true; ModuleId decoder_module = ModuleId::INVALID(); /* 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 only 1 configurable child, * there is no frame decoder here, we can pass on addr code directly */ if (1 == num_configurable_children) { add_addr_code = false; } else { /* For more than 2 children, there is a decoder in the tail of the list * We will not decode that, but will access the address size from that * module So, we reduce the number of children by 1 */ VTR_ASSERT(2 < num_configurable_children); num_configurable_children--; decoder_module = configurable_children.back(); /* The max address code size is the max address code size of all the * configurable children in all the regions */ for (const ModuleId& child_module : module_manager.configurable_children(parent_module)) { /* Bypass any decoder module (which no configurable children */ if (module_manager.configurable_children(child_module).empty()) { continue; } const ModulePortId& child_addr_port_id = module_manager.find_module_port( child_module, std::string(DECODER_ADDRESS_PORT_NAME)); const BasicPort& child_addr_port = module_manager.module_port(child_module, child_addr_port_id); max_child_addr_code_size = std::max(child_addr_port.get_width(), max_child_addr_code_size); } } for (size_t child_id = 0; child_id < num_configurable_children; ++child_id) { ModuleId child_module = configurable_children[child_id]; size_t child_instance = configurable_child_instances[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)); /* Pass on the list of blocks, modules and address lists */ std::vector child_blocks = parent_blocks; child_blocks.push_back(child_block); std::vector child_modules = parent_modules; child_modules.push_back(child_module); /* Set address, apply binary conversion from the first to the last element * in the address list */ std::vector child_addr_code = addr_code; if (true == add_addr_code) { /* Find the address port from the decoder module */ const ModulePortId& decoder_addr_port_id = module_manager.find_module_port( decoder_module, std::string(DECODER_ADDRESS_PORT_NAME)); const BasicPort& decoder_addr_port = module_manager.module_port(decoder_module, decoder_addr_port_id); std::vector addr_bits_vec = itobin_charvec(child_id, decoder_addr_port.get_width()); /* For top-level module, the child address should be added to the tail * For other modules, the child address should be added to the head */ if (top_module == parent_module) { child_addr_code.insert(child_addr_code.end(), addr_bits_vec.begin(), addr_bits_vec.end()); } else { VTR_ASSERT_SAFE(top_module != parent_module); child_addr_code.insert(child_addr_code.begin(), addr_bits_vec.begin(), addr_bits_vec.end()); } /* Note that the address port size of the child module may be smaller * than the maximum of other child modules at this level. We will add * dummy '0's to the head of addr_bit_vec. * * For example: * Decoder is the decoder to access all the child modules * whose address is decoded by the addr_bits_vec * The child modules may use part of the address lines, * we should add dummy '0' to fill the gap * * Addr_code for child[0]: '000' + addr_bits_vec * Addr_code for child[1]: '00' + addr_bits_vec * Addr_code for child[2]: '0' + addr_bits_vec * * Addr[6:8] * | * v * +-------------------------------------------+ * | Decoder Module | * +-------------------------------------------+ * * Addr[0:2] Addr[0:3] Addr[0:4] * | | | * v v v * +-----------+ +-------------+ +------------+ * | Child[0] | | Child[1] | | Child[2] | * +-----------+ +-------------+ +------------+ * * Child[2] has the maximum address lines among the children * */ const ModulePortId& child_addr_port_id = module_manager.find_module_port( child_module, std::string(DECODER_ADDRESS_PORT_NAME)); const BasicPort& child_addr_port = module_manager.module_port(child_module, child_addr_port_id); if (0 < max_child_addr_code_size - child_addr_port.get_width()) { /* Deposit don't care state for the dummy bits */ std::vector dummy_codes( max_child_addr_code_size - child_addr_port.get_width(), bitstream_dont_care_char); child_addr_code.insert(child_addr_code.begin(), dummy_codes.begin(), dummy_codes.end()); } } /* Go recursively */ rec_build_module_fabric_dependent_frame_bitstream( bitstream_manager, child_blocks, module_manager, top_module, config_region, child_modules, child_addr_code, bitstream_dont_care_char, 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. * A leaf node (a memory module) always has a decoder inside * which is the last of configurable children. * We will find the address bit and add it to addr_code * Then we can add the configuration bits to the fabric_bitstream. */ std::vector configurable_children; if (top_module == parent_modules.back()) { configurable_children = module_manager.region_configurable_children( parent_modules.back(), config_region); } else { VTR_ASSERT(top_module != parent_modules.back()); configurable_children = module_manager.configurable_children(parent_modules.back()); } ModuleId decoder_module = configurable_children.back(); /* Find the address port from the decoder module */ const ModulePortId& decoder_addr_port_id = module_manager.find_module_port( decoder_module, std::string(DECODER_ADDRESS_PORT_NAME)); const BasicPort& decoder_addr_port = module_manager.module_port(decoder_module, decoder_addr_port_id); for (size_t ibit = 0; ibit < bitstream_manager.block_bits(parent_blocks.back()).size(); ++ibit) { ConfigBitId config_bit = bitstream_manager.block_bits(parent_blocks.back())[ibit]; std::vector addr_bits_vec = itobin_charvec(ibit, decoder_addr_port.get_width()); std::vector child_addr_code = addr_code; child_addr_code.insert(child_addr_code.begin(), addr_bits_vec.begin(), addr_bits_vec.end()); const FabricBitId& fabric_bit = fabric_bitstream.add_bit(config_bit); /* Set address */ fabric_bitstream.set_bit_address(fabric_bit, child_addr_code); /* 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); } } /******************************************************************** * Main function to build a fabric-dependent bitstream * by considering the configuration protocol types *******************************************************************/ static void build_module_fabric_dependent_bitstream( const ConfigProtocol& config_protocol, const CircuitLibrary& circuit_lib, const BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, const ModuleManager& module_manager, const ModuleId& top_module, FabricBitstream& fabric_bitstream) { switch (config_protocol.type()) { case CONFIG_MEM_STANDALONE: { /* Reserve bits before build-up */ fabric_bitstream.reserve_bits(bitstream_manager.num_bits()); for (const ConfigRegionId& config_region : module_manager.regions(top_module)) { FabricBitRegionId fabric_bitstream_region = fabric_bitstream.add_region(); rec_build_module_fabric_dependent_chain_bitstream( bitstream_manager, top_block, module_manager, top_module, top_module, config_region, fabric_bitstream, fabric_bitstream_region); } break; } case CONFIG_MEM_SCAN_CHAIN: { /* Reserve bits before build-up */ fabric_bitstream.reserve_bits(bitstream_manager.num_bits()); for (const ConfigRegionId& config_region : module_manager.regions(top_module)) { FabricBitRegionId fabric_bitstream_region = fabric_bitstream.add_region(); rec_build_module_fabric_dependent_chain_bitstream( bitstream_manager, top_block, module_manager, top_module, top_module, config_region, fabric_bitstream, fabric_bitstream_region); fabric_bitstream.reverse_region_bits(fabric_bitstream_region); } break; } case CONFIG_MEM_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_memory_bank_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); } break; } case CONFIG_MEM_QL_MEMORY_BANK: { build_module_fabric_dependent_bitstream_ql_memory_bank( config_protocol, circuit_lib, bitstream_manager, top_block, module_manager, top_module, fabric_bitstream); break; } case CONFIG_MEM_FRAME_BASED: { /* Find address port size */ ModulePortId addr_port = module_manager.find_module_port( top_module, std::string(DECODER_ADDRESS_PORT_NAME)); BasicPort addr_port_info = module_manager.module_port(top_module, addr_port); /* Reserve bits before build-up */ fabric_bitstream.set_use_address(true); fabric_bitstream.reserve_bits(bitstream_manager.num_bits()); fabric_bitstream.set_address_length(addr_port_info.get_width()); /* Avoid use don't care if there is only a region */ char bitstream_dont_care_char = DONT_CARE_CHAR; if (1 == module_manager.regions(top_module).size()) { bitstream_dont_care_char = '0'; } /* Find the maximum decoder address among all the configurable regions */ size_t max_decoder_addr_size = 0; for (const ConfigRegionId& config_region : module_manager.regions(top_module)) { std::vector configurable_children = module_manager.region_configurable_children(top_module, config_region); /* Bypass the regions that have no decoders */ if ((0 == configurable_children.size()) || (1 == configurable_children.size())) { continue; } ModuleId decoder_module = configurable_children.back(); ModulePortId decoder_addr_port_id = module_manager.find_module_port( decoder_module, DECODER_ADDRESS_PORT_NAME); BasicPort decoder_addr_port = module_manager.module_port(decoder_module, decoder_addr_port_id); max_decoder_addr_size = std::max(max_decoder_addr_size, decoder_addr_port.get_width()); } for (const ConfigRegionId& config_region : module_manager.regions(top_module)) { std::vector configurable_children = module_manager.region_configurable_children(top_module, config_region); /* Bypass non-configurable regions */ if (0 == configurable_children.size()) { continue; } /* Find the idle address bit which should be added to the head of the * address bit This depends on the number of address bits required by * this region For example: Top-level address is addr[0:4] There are 4 * decoders in the top-level module, whose address sizes are decoder A: * addr[0:4] decoder B: addr[0:3] decoder C: addr[0:2] decoder D: * addr[0:3] For decoder A, the address fit well For decoder B, an idle * bit should be added '0' + addr[0:3] For decoder C, two idle bits * should be added '00' + addr[0:2] For decoder D, an idle bit should be * added '0' + addr[0:3] */ ModuleId decoder_module = configurable_children.back(); ModulePortId decoder_addr_port_id = module_manager.find_module_port( decoder_module, DECODER_ADDRESS_PORT_NAME); BasicPort decoder_addr_port = module_manager.module_port(decoder_module, decoder_addr_port_id); VTR_ASSERT(max_decoder_addr_size >= decoder_addr_port.get_width()); std::vector idle_addr_bits( max_decoder_addr_size - decoder_addr_port.get_width(), bitstream_dont_care_char); FabricBitRegionId fabric_bitstream_region = fabric_bitstream.add_region(); rec_build_module_fabric_dependent_frame_bitstream( bitstream_manager, std::vector(1, top_block), module_manager, top_module, config_region, std::vector(1, top_module), idle_addr_bits, bitstream_dont_care_char, fabric_bitstream, fabric_bitstream_region); } break; } default: VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid SRAM organization.\n"); exit(1); } /* Time-consuming sanity check: Uncomment these codes only for debugging!!! * Check which configuration bits are not touched */ /* for (const ConfigBitId& config_bit : bitstream_manager.bits()) { std::vector::iterator it = std::find(fabric_bitstream.begin(), fabric_bitstream.end(), config_bit); if (it == fabric_bitstream.end()) { std::vector block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, bitstream_manager.bit_parent_block(config_bit)); std::string block_hierarchy_name; for (const ConfigBlockId& temp_block : block_hierarchy) { block_hierarchy_name += std::string("/") + bitstream_manager.block_name(temp_block); } vpr_printf(TIO_MESSAGE_INFO, "bit (parent_block = %s) is not touched!\n", block_hierarchy_name.c_str()); } } */ /* Ensure our fabric bitstream is in the same size as device bistream */ VTR_ASSERT(bitstream_manager.num_bits() == fabric_bitstream.num_bits()); } /******************************************************************** * A top-level function re-organizes the bitstream for a specific * FPGA fabric, where configuration bits are organized in the sequence * that can be directly loaded to the FPGA configuration protocol. * Support: * 1. Configuration chain * 2. Memory decoders * This function does NOT modify the bitstream database * Instead, it builds a vector of ids for configuration bits in bitstream *manager * * This function can be called ONLY after the function build_device_bitstream() * Note that this function does NOT decode bitstreams from circuit *implementation It was done in the function build_device_bitstream() *******************************************************************/ FabricBitstream build_fabric_dependent_bitstream( const BitstreamManager& bitstream_manager, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const bool& verbose) { FabricBitstream fabric_bitstream; vtr::ScopedStartFinishTimer timer("\nBuild fabric dependent bitstream\n"); /* Get the top module name in module manager, which is our starting point */ std::string top_module_name = generate_fpga_top_module_name(); ModuleId top_module = module_manager.find_module(top_module_name); VTR_ASSERT(true == module_manager.valid_module_id(top_module)); /* Find the top block in bitstream manager, which has not parents */ std::vector top_block = find_bitstream_manager_top_blocks(bitstream_manager); /* Make sure we have only 1 top block and its name matches the top module */ VTR_ASSERT(1 == top_block.size()); VTR_ASSERT( 0 == top_module_name.compare(bitstream_manager.block_name(top_block[0]))); /* Start build-up formally */ build_module_fabric_dependent_bitstream( config_protocol, circuit_lib, bitstream_manager, top_block[0], module_manager, top_module, fabric_bitstream); VTR_LOGV(verbose, "Built %lu configuration bits for fabric\n", fabric_bitstream.num_bits()); return fabric_bitstream; } } /* end namespace openfpga */