diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 50a1a7f15..8a86d4d69 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -781,7 +781,12 @@ void add_top_module_sram_ports(ModuleManager& module_manager, BasicPort en_port(std::string(DECODER_ENABLE_PORT_NAME), 1); module_manager.add_port(module_id, en_port, ModuleManager::MODULE_INPUT_PORT); - BasicPort addr_port(std::string(DECODER_ADDRESS_PORT_NAME), total_num_config_bits); + size_t max_num_config_bits = 0; + for (const size_t& curr_num_config_bits : num_config_bits) { + max_num_config_bits = std::max(max_num_config_bits, curr_num_config_bits); + } + + BasicPort addr_port(std::string(DECODER_ADDRESS_PORT_NAME), max_num_config_bits); module_manager.add_port(module_id, addr_port, ModuleManager::MODULE_INPUT_PORT); BasicPort din_port(std::string(DECODER_DATA_IN_PORT_NAME), sram_port_size); @@ -1258,6 +1263,343 @@ void add_top_module_nets_cmos_memory_chain_config_bus(ModuleManager& module_mana } } +/******************************************************************** + * This function will create nets for the following types of connections: + * - Connect the enable signal to the EN of memory module + * - Connect the address port to the address port of memory module + * - Connect the data_in (Din) to the data_in of the memory module + * Note that the top-level module may have multiple regions and + * therefore the Din port have multiple pins. The Din of local decoder + * should be connected the Din pin indexed by current configuration region + * + * EN ADDR[X-1:0] DATA_IN[N-1:0] + * | | | + * | | | Top module + * +----+-----+------------+------------------ + * | | | | + * | v v v + * | +-------------------------------+ + * | | EN ADDR[X-1:0] DATA_IN[N-1:0] | + * | | | + * | | Configurable Child | + * | | | + * | +-------------------------------+ + * + * Note: + * - This function is ONLY applicable to single configurable child case!!! + * - This function is applicable to the configurable child in a specific region!!! + *********************************************************************/ +static +void add_top_module_nets_cmos_memory_frame_short_config_bus(ModuleManager& module_manager, + const ModuleId& top_module, + const ConfigRegionId& config_region) { + std::vector configurable_children = module_manager.region_configurable_children(top_module, config_region); + + VTR_ASSERT(1 == configurable_children.size()); + ModuleId child_module = configurable_children[0]; + size_t child_instance = module_manager.region_configurable_child_instances(top_module, config_region)[0]; + + /* Connect the enable (EN) port of the parent module + * to the EN port of memory module + */ + ModulePortId parent_en_port = module_manager.find_module_port(top_module, std::string(DECODER_ENABLE_PORT_NAME)); + ModulePortId child_en_port = module_manager.find_module_port(child_module, std::string(DECODER_ENABLE_PORT_NAME)); + add_module_bus_nets(module_manager, top_module, + top_module, 0, parent_en_port, + child_module, child_instance, child_en_port); + + /* Connect the address port of the parent module to the child module address port */ + ModulePortId parent_addr_port = module_manager.find_module_port(top_module, std::string(DECODER_ADDRESS_PORT_NAME)); + ModulePortId child_addr_port = module_manager.find_module_port(child_module, std::string(DECODER_ADDRESS_PORT_NAME)); + add_module_bus_nets(module_manager, top_module, + top_module, 0, parent_addr_port, + child_module, child_instance, child_addr_port); + + /* Connect the data_in (Din) of parent module to the data_in of the memory module + */ + ModulePortId parent_din_port = module_manager.find_module_port(top_module, std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort parent_din_port_info = module_manager.module_port(top_module, parent_din_port); + ModulePortId child_din_port = module_manager.find_module_port(child_module, std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort child_din_port_info = module_manager.module_port(child_module, child_din_port); + + /* Ensure pin indices are in range! */ + VTR_ASSERT(size_t(config_region) < parent_din_port_info.get_width()); + VTR_ASSERT(1 == child_din_port_info.get_width()); + + /* Create a net for the Din[config_region] pin */ + ModuleNetId din_net = create_module_source_pin_net(module_manager, top_module, + top_module, 0, + parent_din_port, + parent_din_port_info.pins()[size_t(config_region)]); + VTR_ASSERT(ModuleNetId::INVALID() != din_net); + + /* Configure the net sink */ + module_manager.add_module_net_sink(top_module, din_net, child_module, child_instance, child_din_port, child_din_port_info.pins()[0]); +} + +/******************************************************************** + * This function will + * - Add a frame decoder to the parent module + * - If the decoder exists in the library, we use the module + * - If the decoder does not exist, we create a new module and use it + * - Create nets for the following types of connections: + * - Connect the EN signal, first few bits of address of parent module + * to the frame decoder inputs + * Note that the top-level module may have more address bits than + * what is required for this configuration region. + * A decoder will be created anyway to avoid address collision + * to other configuration regions + * - Connect the enable (EN) port of memory modules under the parent module + * to the frame decoder outputs + * - Connect the data_in (Din) of parent module to the data_in of the all + * the memory modules + * Note that the top-level module may have multiple regions and + * therefore the Din port have multiple pins. The Din of local decoder + * should be connected the Din pin indexed by current configuration region + * + * EN ADDR[X-1:0] DATA_IN[Y-1:0] + * | | | + * | | | Top module + * +--------+-------+------------+------------------ + * | | | + * | v v + * | EN ADDR[X - 1: X - log(N)/log2] + * | | | + * | v v + * | +--------------------------------------------+ + * | | Frame-based decoder | + * | | | + * | | Data out | + * | +--------------------------------------------+ + * | | + * | +-------------+--------------------+ + * | | | | + * | Din | Din | Din | + * | [Y] | [Y] | [Y] | + * | | | | | | | + * | v v v v v v + * | +--------+ +--------+ +--------+ + * | | Memory | | Memory | ... | Memory | + * | | Module | | Module | | Module | + * | | [0] | | [1] | | [N-1] | + * | +--------+ +--------+ +--------+ + * | ^ ^ ^ + * | | | | + * | +-------------+--------------------+ + * | | + * | ADDR[X - log(N)/log2 - 1: 0] + * + * Note: + * - X is the port size of address port of the parent module + * - the address port of child memory modules may be smaller than + * X - log(N)/log2. In such case, we will drop the MSBs until it fit + * - This function is only applicable to 2+ configurable children!!! + * + *********************************************************************/ +static +void add_top_module_nets_cmos_memory_frame_decoder_config_bus(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, + const ModuleId& parent_module, + const ConfigRegionId& config_region) { + std::vector configurable_children = module_manager.region_configurable_children(parent_module, config_region); + std::vector configurable_child_instances = module_manager.region_configurable_child_instances(parent_module, config_region); + + /* Find the decoder specification */ + size_t addr_size = find_mux_local_decoder_addr_size(configurable_children.size()); + /* Data input should match the WL (data_in) of a SRAM */ + size_t data_size = configurable_children.size(); + + /* Find the number of address bits that are wired directly to configurable children */ + size_t max_child_addr_size = 0; + for (size_t mem_index = 0; mem_index < configurable_children.size(); ++mem_index) { + ModuleId child_module = configurable_children[mem_index]; + ModulePortId child_addr_port = module_manager.find_module_port(child_module, std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort child_addr_port_info = module_manager.module_port(child_module, child_addr_port); + max_child_addr_size = std::max(max_child_addr_size, child_addr_port_info.get_width()); + } + + /* Search the decoder library and try to find one + * If not found, create a new module and add it to the module manager + */ + DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, true, false, false); + if (DecoderId::INVALID() == decoder_id) { + decoder_id = decoder_lib.add_decoder(addr_size, data_size, true, false, false); + } + VTR_ASSERT(DecoderId::INVALID() != decoder_id); + + /* Create a module if not existed yet */ + std::string decoder_module_name = generate_memory_decoder_subckt_name(addr_size, data_size); + ModuleId decoder_module = module_manager.find_module(decoder_module_name); + if (ModuleId::INVALID() == decoder_module) { + decoder_module = build_frame_memory_decoder_module(module_manager, + decoder_lib, + decoder_id); + } + VTR_ASSERT(ModuleId::INVALID() != decoder_module); + + /* Instanciate the decoder module here */ + size_t decoder_instance = module_manager.num_instance(parent_module, decoder_module); + module_manager.add_child_module(parent_module, decoder_module); + + /* Connect the enable (EN) port of memory modules under the parent module + * to the frame decoder inputs + */ + ModulePortId parent_en_port = module_manager.find_module_port(parent_module, std::string(DECODER_ENABLE_PORT_NAME)); + ModulePortId decoder_en_port = module_manager.find_module_port(decoder_module, std::string(DECODER_ENABLE_PORT_NAME)); + add_module_bus_nets(module_manager, parent_module, + parent_module, 0, parent_en_port, + decoder_module, decoder_instance, decoder_en_port); + + /* Connect the address port of the parent module to the frame decoder address port + * Note that we only connect to the first few bits of address port + */ + ModulePortId parent_addr_port = module_manager.find_module_port(parent_module, std::string(DECODER_ADDRESS_PORT_NAME)); + ModulePortId decoder_addr_port = module_manager.find_module_port(decoder_module, std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort parent_addr_port_info = module_manager.module_port(parent_module, parent_addr_port); + BasicPort decoder_addr_port_info = module_manager.module_port(decoder_module, decoder_addr_port); + for (size_t ipin = 0; ipin < decoder_addr_port_info.get_width(); ++ipin) { + /* Create a net for the addr pin */ + ModuleNetId addr_net = create_module_source_pin_net(module_manager, parent_module, + parent_module, 0, + parent_addr_port, + parent_addr_port_info.pins()[ipin + max_child_addr_size]); + VTR_ASSERT(ModuleNetId::INVALID() != addr_net); + + /* Configure the net sink */ + module_manager.add_module_net_sink(parent_module, addr_net, + decoder_module, decoder_instance, + decoder_addr_port, + decoder_addr_port_info.pins()[ipin]); + } + + /* Connect the address port of the parent module to the address port of configurable children + * Note that we only connect to the last few bits of address port + */ + for (size_t mem_index = 0; mem_index < configurable_children.size(); ++mem_index) { + ModuleId child_module = configurable_children[mem_index]; + size_t child_instance = configurable_child_instances[mem_index]; + ModulePortId child_addr_port = module_manager.find_module_port(child_module, std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort child_addr_port_info = module_manager.module_port(child_module, child_addr_port); + for (size_t ipin = 0; ipin < child_addr_port_info.get_width(); ++ipin) { + ModuleNetId addr_net = create_module_source_pin_net(module_manager, parent_module, + parent_module, 0, + parent_addr_port, + parent_addr_port_info.pins()[ipin]); + VTR_ASSERT(ModuleNetId::INVALID() != addr_net); + + /* Configure the net sink */ + module_manager.add_module_net_sink(parent_module, addr_net, + child_module, child_instance, + child_addr_port, + child_addr_port_info.pins()[ipin]); + } + } + + /* Connect the data_in (Din) of parent module to the data_in of the all + * the memory modules + */ + ModulePortId parent_din_port = module_manager.find_module_port(parent_module, std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort parent_din_port_info = module_manager.module_port(parent_module, parent_din_port); + for (size_t mem_index = 0; mem_index < configurable_children.size(); ++mem_index) { + ModuleId child_module = configurable_children[mem_index]; + size_t child_instance = module_manager.configurable_child_instances(parent_module)[mem_index]; + ModulePortId child_din_port = module_manager.find_module_port(child_module, std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort child_din_port_info = module_manager.module_port(child_module, child_din_port); + + /* Ensure pin indices are in range! */ + VTR_ASSERT(size_t(config_region) < parent_din_port_info.get_width()); + VTR_ASSERT(1 == child_din_port_info.get_width()); + + /* Create a net for the Din[config_region] pin */ + ModuleNetId din_net = create_module_source_pin_net(module_manager, parent_module, + parent_module, 0, + parent_din_port, + parent_din_port_info.pins()[size_t(config_region)]); + VTR_ASSERT(ModuleNetId::INVALID() != din_net); + + /* Configure the net sink */ + module_manager.add_module_net_sink(parent_module, din_net, child_module, child_instance, child_din_port, child_din_port_info.pins()[0]); + } + + /* Connect the data_out port of the decoder module + * to the enable port of configurable children + */ + ModulePortId decoder_dout_port = module_manager.find_module_port(decoder_module, std::string(DECODER_DATA_OUT_PORT_NAME)); + BasicPort decoder_dout_port_info = module_manager.module_port(decoder_module, decoder_dout_port); + VTR_ASSERT(decoder_dout_port_info.get_width() == configurable_children.size()); + for (size_t mem_index = 0; mem_index < configurable_children.size(); ++mem_index) { + ModuleId child_module = configurable_children[mem_index]; + size_t child_instance = module_manager.configurable_child_instances(parent_module)[mem_index]; + ModulePortId child_en_port = module_manager.find_module_port(child_module, std::string(DECODER_ENABLE_PORT_NAME)); + BasicPort child_en_port_info = module_manager.module_port(child_module, child_en_port); + for (size_t ipin = 0; ipin < child_en_port_info.get_width(); ++ipin) { + ModuleNetId en_net = create_module_source_pin_net(module_manager, parent_module, + decoder_module, decoder_instance, + decoder_dout_port, + decoder_dout_port_info.pins()[mem_index]); + VTR_ASSERT(ModuleNetId::INVALID() != en_net); + + /* Configure the net sink */ + module_manager.add_module_net_sink(parent_module, en_net, + child_module, child_instance, + child_en_port, + child_en_port_info.pins()[ipin]); + } + } + + /* Add the decoder as the last configurable children */ + module_manager.add_configurable_child(parent_module, decoder_module, decoder_instance); + /* Register the configurable child to configuration region */ + module_manager.add_configurable_child_to_region(parent_module, + config_region, + decoder_module, + decoder_instance, + module_manager.configurable_children(parent_module).size() - 1); +} + +/********************************************************************* + * Add framed-based decoders to the top-level module + * and build net connections between decoders and subblocks + * + * For each configuration region, we create an independent decoder + * Note that to avoid parasitic programming, all the decoders will + * be in the same size, sharing the same principle as memory banks + * + * For each region, decoder and net addition will depend on the following cases: + * - If there is no configurable child, nothing to do. + * - If there is only one configurable child, short wire the EN, ADDR and DATA_IN to it + * - If there are more than two configurable childern, add a decoder and build interconnection + * between it and the children + **********************************************************************/ +static +void add_top_module_nets_cmos_memory_frame_config_bus(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, + const ModuleId& top_module, + const vtr::vector& num_config_bits) { + /* Find the number of address bits for the top-level module */ + size_t top_addr_size = 0; + for (const ConfigRegionId& config_region : module_manager.regions(top_module)) { + top_addr_size = std::max(top_addr_size, num_config_bits[config_region]); + } + + for (const ConfigRegionId& config_region : module_manager.regions(top_module)) { + if (0 == module_manager.region_configurable_children(top_module, config_region).size()) { + continue; + } + + /* Short-wiring is applicable only when all the following situations are met: + * - There is only 1 configurable child in the region + * - The number of address bits of the configurable child is the same as top-level + */ + if ( (1 == module_manager.region_configurable_children(top_module, config_region).size()) + && (num_config_bits[config_region] == top_addr_size)) { + add_top_module_nets_cmos_memory_frame_short_config_bus(module_manager, top_module, config_region); + } else { + add_top_module_nets_cmos_memory_frame_decoder_config_bus(module_manager, decoder_lib, top_module, config_region); + } + } +} + /********************************************************************* * Add the port-to-port connection between all the memory modules * and their parent module @@ -1323,7 +1665,7 @@ void add_top_module_nets_cmos_memory_config_bus(ModuleManager& module_manager, add_top_module_nets_cmos_memory_bank_config_bus(module_manager, decoder_lib, parent_module, num_config_bits); break; case CONFIG_MEM_FRAME_BASED: - add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, parent_module); + add_top_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, parent_module, num_config_bits); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index e4ded1821..5172bbef9 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -85,14 +85,13 @@ size_t rec_estimate_device_bitstream_num_bits(const ModuleManager& module_manage 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(); - /* FIXME: This will be uncommented when multi-region support is extended for frame-based - * Frame-based configuration protocol will have 1 decoder + /* 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 <= curr_region_num_config_child)) { curr_region_num_config_child--; } - */ /* Memory configuration protocol will have 2 decoders * at the top-level diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index 04d7fe714..311995a72 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -281,6 +281,8 @@ 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, FabricBitstream& fabric_bitstream, @@ -293,7 +295,18 @@ void rec_build_module_fabric_dependent_frame_bitstream(const BitstreamManager& b const ConfigBlockId& parent_block = parent_blocks.back(); const ModuleId& parent_module = parent_modules.back(); - size_t num_configurable_children = module_manager.configurable_children(parent_modules.back()).size(); + 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; @@ -318,11 +331,11 @@ void rec_build_module_fabric_dependent_frame_bitstream(const BitstreamManager& b */ VTR_ASSERT(2 < num_configurable_children); num_configurable_children--; - decoder_module = module_manager.configurable_children(parent_module).back(); + decoder_module = configurable_children.back(); /* The address code size is the max. of address port of all the configurable children */ for (size_t child_id = 0; child_id < num_configurable_children; ++child_id) { - ModuleId child_module = module_manager.configurable_children(parent_module)[child_id]; + ModuleId child_module = configurable_children[child_id]; 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((int)child_addr_port.get_width(), (int)max_child_addr_code_size); @@ -330,15 +343,14 @@ void rec_build_module_fabric_dependent_frame_bitstream(const BitstreamManager& b } for (size_t child_id = 0; child_id < num_configurable_children; ++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]; + 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! */ - if (true != bitstream_manager.valid_block_id(child_block)) VTR_ASSERT(true == bitstream_manager.valid_block_id(child_block)); /* Pass on the list of blocks, modules and address lists */ @@ -400,7 +412,10 @@ void rec_build_module_fabric_dependent_frame_bitstream(const BitstreamManager& b /* Go recursively */ rec_build_module_fabric_dependent_frame_bitstream(bitstream_manager, child_blocks, - module_manager, child_modules, + module_manager, + top_module, + config_region, + child_modules, child_addr_code, fabric_bitstream, fabric_bitstream_region); @@ -417,9 +432,15 @@ void rec_build_module_fabric_dependent_frame_bitstream(const BitstreamManager& b * We will find the address bit and add it to addr_code * Then we can add the configuration bits to the fabric_bitstream. */ - if (!(1 < module_manager.configurable_children(parent_modules.back()).size())) - VTR_ASSERT(1 < module_manager.configurable_children(parent_modules.back()).size()); - ModuleId decoder_module = module_manager.configurable_children(parent_modules.back()).back(); + 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); @@ -557,6 +578,8 @@ void build_module_fabric_dependent_bitstream(const ConfigProtocol& config_protoc 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), std::vector(), fabric_bitstream, diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index b6da8f813..1d2d9c83c 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -1676,29 +1676,74 @@ void print_verilog_top_testbench_frame_decoder_bitstream(std::fstream& fp, fp << std::endl; - /* Attention: the configuration chain protcol requires the last configuration bit is fed first - * We will visit the fabric bitstream in a reverse way + /* Reorganize the fabric bitstream by the same address across regions: + * This is due to that the length of fabric bitstream could be different in each region. + * Template: + *
+ * An example: + * 000000 1011 + * + * Note: the std::map may cause large memory footprint for large bitstream databases! */ - for (const FabricBitId& bit_id : fabric_bitstream.bits()) { - /* When fast configuration is enabled, we skip zero data_in values */ - if ((true == fast_configuration) - && (bit_value_to_skip == fabric_bitstream.bit_din(bit_id))) { - continue; + std::map> fabric_bits_by_addr; + for (const FabricBitRegionId& region : fabric_bitstream.regions()) { + for (const FabricBitId& bit_id : fabric_bitstream.region_bits(region)) { + /* Create string for address */ + VTR_ASSERT(addr_port.get_width() == fabric_bitstream.bit_address(bit_id).size()); + std::string addr_str; + for (const char& addr_bit : fabric_bitstream.bit_address(bit_id)) { + addr_str.push_back(addr_bit); + } + + /* Place the config bit */ + auto result = fabric_bits_by_addr.find(addr_str); + if (result == fabric_bits_by_addr.end()) { + /* This is a new bit, resize the vector to the number of regions + * and deposit '0' to all the bits + */ + fabric_bits_by_addr[addr_str] = std::vector(fabric_bitstream.regions().size(), false); + fabric_bits_by_addr[addr_str][size_t(region)] = fabric_bitstream.bit_din(bit_id); + } else { + VTR_ASSERT_SAFE(result != fabric_bits_by_addr.end()); + result->second[size_t(region)] = fabric_bitstream.bit_din(bit_id); + } + } + } + + for (const auto& addr_din_pair : fabric_bits_by_addr) { + /* When fast configuration is enabled, + * the rule to skip any configuration bit should consider the whole data input values. + * Only all the bits in the din port match the value to be skipped, + * the programming cycle can be skipped! + */ + if (true == fast_configuration) { + bool skip_curr_bits = true; + for (const bool& bit : addr_din_pair.second) { + if (bit_value_to_skip != bit) { + skip_curr_bits = false; + break; + } + } + + if (true == skip_curr_bits) { + continue; + } } fp << "\t\t" << std::string(TOP_TESTBENCH_PROG_TASK_NAME); fp << "(" << addr_port.get_width() << "'b"; - VTR_ASSERT(addr_port.get_width() == fabric_bitstream.bit_address(bit_id).size()); - for (const char& addr_bit : fabric_bitstream.bit_address(bit_id)) { - fp << addr_bit; - } + VTR_ASSERT(addr_port.get_width() == addr_din_pair.first.size()); + fp << addr_din_pair.first; fp << ", "; fp <<"1'b"; - if (true == fabric_bitstream.bit_din(bit_id)) { - fp << "1"; - } else { - VTR_ASSERT(false == fabric_bitstream.bit_din(bit_id)); - fp << "0"; + VTR_ASSERT(din_port.get_width() == addr_din_pair.second.size()); + for (const bool& din_value : addr_din_pair.second) { + if (true == din_value) { + fp << "1"; + } else { + VTR_ASSERT(false == din_value); + fp << "0"; + } } fp << ");" << std::endl; }