/******************************************************************** * This file includes functions to build bitstream from a mapped * FPGA fabric. * We decode the bitstream from configuration of routing multiplexers * and Look-Up Tables (LUTs) which locate in CLBs and global routing *architecture *******************************************************************/ #include /* Headers from vtrutil library */ #include "build_device_bitstream.h" #include "build_grid_bitstream.h" #include "build_routing_bitstream.h" #include "memory_utils.h" #include "module_manager_utils.h" #include "openfpga_naming.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_time.h" /* begin namespace openfpga */ namespace openfpga { /******************************************************************** * Estimate the number of blocks to be added to the whole device bitstream * This function will recursively walk through the module graph * from the specified top module and count the number of configurable children * which are the blocks that will be added to the bitstream manager *******************************************************************/ static size_t rec_estimate_device_bitstream_num_blocks( const ModuleManager& module_manager, const ModuleId& top_module) { size_t num_blocks = 0; /* Those child modules which have no children are * actually configurable memory elements * We skip them in couting */ if (0 == module_manager.configurable_children(top_module).size()) { return 0; } size_t num_configurable_children = module_manager.configurable_children(top_module).size(); for (size_t ichild = 0; ichild < num_configurable_children; ++ichild) { ModuleId child_module = module_manager.configurable_children(top_module)[ichild]; num_blocks += rec_estimate_device_bitstream_num_blocks(module_manager, child_module); } /* Add the number of blocks at current level */ num_blocks++; return num_blocks; } /******************************************************************** * Estimate the number of configuration bits to be added to the whole device *bitstream This function will recursively walk through the module graph from *the specified top module and count the number of leaf configurable children * which are the bits that will be added to the bitstream manager *******************************************************************/ static size_t rec_estimate_device_bitstream_num_bits( const ModuleManager& module_manager, const ModuleId& top_module, const ModuleId& parent_module, const ConfigProtocol& config_protocol) { size_t num_bits = 0; /* If a child module has no configurable children, this is a leaf node * We can count it in. Otherwise, we should go recursively. */ if (0 == module_manager.configurable_children(parent_module).size()) { return 1; } /* Two cases to walk through configurable children: * - For top-level module: * Iterate over the multiple regions and visit each configuration child * under any region In each region, frame-based configuration protocol or * memory bank protocol will contain decoders. We should bypass them when * count the bitstream size * - For other modules: * Iterate over the configurable children regardless of regions */ if (parent_module == top_module) { for (const ConfigRegionId& config_region : module_manager.regions(parent_module)) { size_t curr_region_num_config_child = module_manager .region_configurable_children(parent_module, config_region) .size(); size_t num_child_to_skip = estimate_num_configurable_children_to_skip_by_config_protocol( config_protocol, curr_region_num_config_child); curr_region_num_config_child -= num_child_to_skip; /* Visit all the children in a recursively way */ for (size_t ichild = 0; ichild < curr_region_num_config_child; ++ichild) { ModuleId child_module = module_manager.region_configurable_children( parent_module, config_region)[ichild]; num_bits += rec_estimate_device_bitstream_num_bits( module_manager, top_module, child_module, config_protocol); } } } else { VTR_ASSERT_SAFE(parent_module != top_module); size_t num_configurable_children = module_manager.configurable_children(parent_module).size(); /* Frame-based configuration protocol will have 1 decoder * if there are more than 1 configurable children */ if ((CONFIG_MEM_FRAME_BASED == config_protocol.type()) && (2 <= num_configurable_children)) { num_configurable_children--; } for (size_t ichild = 0; ichild < num_configurable_children; ++ichild) { ModuleId child_module = module_manager.configurable_children(parent_module)[ichild]; num_bits += rec_estimate_device_bitstream_num_bits( module_manager, top_module, child_module, config_protocol); } } return num_bits; } /******************************************************************** * A top-level function to build a bistream from the FPGA device * 1. It will organize the bitstream w.r.t. the hierarchy of module graphs * describing the FPGA fabric * 2. It will decode configuration bits from routing multiplexers used in * global routing architecture * 3. It will decode configuration bits from routing multiplexers and LUTs * used in CLBs * * Note: this function create a bitstream which is binding to the module graphs * of the FPGA fabric that FPGA-X2P generates! * But it can be used to output a generic bitstream for VPR mapping FPGA *******************************************************************/ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, const OpenfpgaContext& openfpga_ctx, const bool& verbose) { std::string timer_message = std::string("\nBuild fabric-independent bitstream for implementation '") + vpr_ctx.atom().nlist.netlist_name() + std::string("'\n"); vtr::ScopedStartFinishTimer timer(timer_message); /* Bitstream manager to be built */ BitstreamManager bitstream_manager; /* Create the top-level block for bitstream * This is related to the top-level module of fpga */ std::string top_block_name = generate_fpga_top_module_name(); ConfigBlockId top_block = bitstream_manager.add_block(top_block_name); ModuleId top_module = openfpga_ctx.module_graph().find_module(top_block_name); VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module)); /* Create the core block when the fpga_core is added */ size_t num_blocks_to_reserve = 0; std::string core_block_name = generate_fpga_core_module_name(); const ModuleId& core_module = openfpga_ctx.module_graph().find_module(core_block_name); if (openfpga_ctx.module_graph().valid_module_id(core_module)) { std::string core_inst_name = openfpga_ctx.module_graph().instance_name(top_module, core_module, 0); ConfigBlockId core_block = bitstream_manager.add_block(core_inst_name); bitstream_manager.add_child_block(top_block, core_block); /* Now we use the core_block as the top-level block for the remaining * functions */ top_module = core_module; top_block = core_block; /* Count in fpga core as a block to reserve */ num_blocks_to_reserve += 1; } /* Estimate the number of blocks to be added to the database */ num_blocks_to_reserve += rec_estimate_device_bitstream_num_blocks( openfpga_ctx.module_graph(), top_module); bitstream_manager.reserve_blocks(num_blocks_to_reserve); VTR_LOGV(verbose, "Reserved %lu configurable blocks\n", num_blocks_to_reserve); /* Estimate the number of bits to be added to the database */ size_t num_bits_to_reserve = rec_estimate_device_bitstream_num_bits( openfpga_ctx.module_graph(), top_module, top_module, openfpga_ctx.arch().config_protocol); bitstream_manager.reserve_bits(num_bits_to_reserve); VTR_LOGV(verbose, "Reserved %lu configuration bits\n", num_bits_to_reserve); /* Reserve child blocks for the top level block */ bitstream_manager.reserve_child_blocks( top_block, count_module_manager_module_configurable_children( openfpga_ctx.module_graph(), top_module)); /* Create bitstream from grids */ VTR_LOGV(verbose, "Building grid bitstream...\n"); build_grid_bitstream(bitstream_manager, top_block, openfpga_ctx.module_graph(), openfpga_ctx.fabric_tile(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), vpr_ctx.device().grid, vpr_ctx.atom(), openfpga_ctx.vpr_device_annotation(), openfpga_ctx.vpr_clustering_annotation(), openfpga_ctx.vpr_placement_annotation(), openfpga_ctx.vpr_bitstream_annotation(), verbose); VTR_LOGV(verbose, "Done\n"); /* Create bitstream from routing architectures */ VTR_LOGV(verbose, "Building routing bitstream...\n"); build_routing_bitstream( bitstream_manager, top_block, openfpga_ctx.module_graph(), openfpga_ctx.fabric_tile(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), vpr_ctx.atom(), openfpga_ctx.vpr_device_annotation(), openfpga_ctx.vpr_routing_annotation(), vpr_ctx.device().rr_graph, openfpga_ctx.device_rr_gsb(), openfpga_ctx.flow_manager().compress_routing(), verbose); VTR_LOGV(verbose, "Done\n"); VTR_LOGV(verbose, "Decoded %lu configuration bits into %lu blocks\n", bitstream_manager.num_bits(), bitstream_manager.num_blocks()); VTR_ASSERT(num_blocks_to_reserve == bitstream_manager.num_blocks()); VTR_ASSERT(num_bits_to_reserve == bitstream_manager.num_bits()); return bitstream_manager; } } /* end namespace openfpga */