From 2d2b8f67aab3b6f208711b473cf13133751de751 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 31 Jul 2023 17:32:48 -0700 Subject: [PATCH 01/41] [core] adding new option '--group_config_block' to command 'build_fabric' --- .../src/base/openfpga_build_fabric_template.h | 2 + .../base/openfpga_setup_command_template.h | 8 +++ openfpga/src/fabric/build_device_module.cpp | 6 +- openfpga/src/fabric/build_device_module.h | 4 +- openfpga/src/fabric/build_grid_modules.cpp | 72 ++++++++++--------- openfpga/src/fabric/build_grid_modules.h | 16 ++--- 6 files changed, 61 insertions(+), 47 deletions(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 31e9c565a..453c4c3b2 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -100,6 +100,7 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, CommandOptionId opt_write_fabric_key = cmd.option("write_fabric_key"); CommandOptionId opt_load_fabric_key = cmd.option("load_fabric_key"); CommandOptionId opt_group_tile = cmd.option("group_tile"); + CommandOptionId opt_group_config_block = cmd.option("group_config_block"); CommandOptionId opt_verbose = cmd.option("verbose"); /* Report conflicts with options: @@ -175,6 +176,7 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, cmd_context.option_enable(cmd, opt_compress_routing), cmd_context.option_enable(cmd, opt_duplicate_grid_pin), predefined_fabric_key, tile_config, + cmd_context.option_enable(cmd, opt_group_config_block), cmd_context.option_enable(cmd, opt_gen_random_fabric_key), cmd_context.option_enable(cmd, opt_verbose)); diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index 12d34c00d..ad7856732 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -412,6 +412,14 @@ ShellCommandId add_build_fabric_command_template( "reduce the number of blocks at top-level"); shell_cmd.set_option_require_value(opt_group_tile, openfpga::OPT_STRING); + /* Add an option '--group_config_block' */ + CommandOptionId opt_group_config_block = + shell_cmd.add_option("group_config_block", false, + "group configuration memory blocks under CLB/SB/CB " + "blocks etc. This helps to " + "reduce optimize the density of configuration memory " + "through physical design"); + /* Add an option '--generate_random_fabric_key' */ shell_cmd.add_option("generate_random_fabric_key", false, "Create a random fabric key which will shuffle the " diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index b4d9cb222..3b05142a1 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -37,8 +37,8 @@ int build_device_module_graph( const OpenfpgaContext& openfpga_ctx, const DeviceContext& vpr_device_ctx, const bool& frame_view, const bool& compress_routing, const bool& duplicate_grid_pin, const FabricKey& fabric_key, - const TileConfig& tile_config, const bool& generate_random_fabric_key, - const bool& verbose) { + const TileConfig& tile_config, const bool& group_config_block, + const bool& generate_random_fabric_key, const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build fabric module graph"); int status = CMD_EXEC_SUCCESS; @@ -85,7 +85,7 @@ int build_device_module_graph( openfpga_ctx.vpr_device_annotation(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), openfpga_ctx.arch().config_protocol.type(), sram_model, - duplicate_grid_pin, verbose); + duplicate_grid_pin, group_config_block, verbose); if (true == compress_routing) { build_unique_routing_modules( diff --git a/openfpga/src/fabric/build_device_module.h b/openfpga/src/fabric/build_device_module.h index 6244fc7dd..fb4178cc8 100644 --- a/openfpga/src/fabric/build_device_module.h +++ b/openfpga/src/fabric/build_device_module.h @@ -24,8 +24,8 @@ int build_device_module_graph( const OpenfpgaContext& openfpga_ctx, const DeviceContext& vpr_device_ctx, const bool& frame_view, const bool& compress_routing, const bool& duplicate_grid_pin, const FabricKey& fabric_key, - const TileConfig& tile_config, const bool& generate_random_fabric_key, - const bool& verbose); + const TileConfig& tile_config, const bool& group_config_block, + const bool& generate_random_fabric_key, const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index f724abd66..19ea1a9f1 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -265,7 +265,7 @@ static void build_primitive_block_module( const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, t_pb_graph_node* primitive_pb_graph_node, - const bool& verbose) { + const bool& group_config_block, const bool& verbose) { /* Ensure a valid pb_graph_node */ VTR_ASSERT(nullptr != primitive_pb_graph_node); @@ -504,7 +504,7 @@ static void add_module_pb_graph_pin_interc( std::vector& memory_modules, std::vector& memory_instances, const VprDeviceAnnotation& device_annotation, const CircuitLibrary& circuit_lib, t_pb_graph_pin* des_pb_graph_pin, - t_mode* physical_mode) { + t_mode* physical_mode, const bool& group_config_block, const bool& verbose) { /* Find the number of fan-in and detailed interconnection information * related to the destination pb_graph_pin */ @@ -732,7 +732,8 @@ static void add_module_pb_graph_port_interc( std::vector& memory_modules, std::vector& memory_instances, const VprDeviceAnnotation& device_annotation, const CircuitLibrary& circuit_lib, t_pb_graph_node* des_pb_graph_node, - const e_circuit_pb_port_type& pb_port_type, t_mode* physical_mode) { + const e_circuit_pb_port_type& pb_port_type, t_mode* physical_mode, + const bool& group_config_block, const bool& verbose) { switch (pb_port_type) { case CIRCUIT_PB_PORT_INPUT: { for (int iport = 0; iport < des_pb_graph_node->num_input_ports; ++iport) { @@ -742,7 +743,8 @@ static void add_module_pb_graph_port_interc( add_module_pb_graph_pin_interc( module_manager, pb_module, memory_modules, memory_instances, device_annotation, circuit_lib, - &(des_pb_graph_node->input_pins[iport][ipin]), physical_mode); + &(des_pb_graph_node->input_pins[iport][ipin]), physical_mode, + group_config_block, verbose); } } break; @@ -755,7 +757,8 @@ static void add_module_pb_graph_port_interc( add_module_pb_graph_pin_interc( module_manager, pb_module, memory_modules, memory_instances, device_annotation, circuit_lib, - &(des_pb_graph_node->output_pins[iport][ipin]), physical_mode); + &(des_pb_graph_node->output_pins[iport][ipin]), physical_mode, + group_config_block, verbose); } } break; @@ -767,7 +770,8 @@ static void add_module_pb_graph_port_interc( add_module_pb_graph_pin_interc( module_manager, pb_module, memory_modules, memory_instances, device_annotation, circuit_lib, - &(des_pb_graph_node->clock_pins[iport][ipin]), physical_mode); + &(des_pb_graph_node->clock_pins[iport][ipin]), physical_mode, + group_config_block, verbose); } } break; @@ -814,7 +818,8 @@ static void add_module_pb_graph_interc( std::vector& memory_modules, std::vector& memory_instances, const VprDeviceAnnotation& device_annotation, const CircuitLibrary& circuit_lib, t_pb_graph_node* physical_pb_graph_node, - const int& physical_mode_index) { + const int& physical_mode_index, const bool& group_config_block, + const bool& verbose) { /* Check cur_pb_graph_node*/ VTR_ASSERT(nullptr != physical_pb_graph_node); @@ -830,10 +835,10 @@ static void add_module_pb_graph_interc( * | * input_pins, edges, output_pins */ - add_module_pb_graph_port_interc(module_manager, pb_module, memory_modules, - memory_instances, device_annotation, - circuit_lib, physical_pb_graph_node, - CIRCUIT_PB_PORT_OUTPUT, physical_mode); + add_module_pb_graph_port_interc( + module_manager, pb_module, memory_modules, memory_instances, + device_annotation, circuit_lib, physical_pb_graph_node, + CIRCUIT_PB_PORT_OUTPUT, physical_mode, group_config_block, verbose); /* We check input_pins of child_pb_graph_node and its the input_edges * Built the interconnections between inputs of cur_pb_graph_node and inputs @@ -856,16 +861,16 @@ static void add_module_pb_graph_interc( &(physical_pb_graph_node ->child_pb_graph_nodes[physical_mode_index][child][inst]); /* For each child_pb_graph_node input pins*/ - add_module_pb_graph_port_interc(module_manager, pb_module, memory_modules, - memory_instances, device_annotation, - circuit_lib, child_pb_graph_node, - CIRCUIT_PB_PORT_INPUT, physical_mode); + add_module_pb_graph_port_interc( + module_manager, pb_module, memory_modules, memory_instances, + device_annotation, circuit_lib, child_pb_graph_node, + CIRCUIT_PB_PORT_INPUT, physical_mode, group_config_block, verbose); /* For each child_pb_graph_node clock pins*/ - add_module_pb_graph_port_interc(module_manager, pb_module, memory_modules, - memory_instances, device_annotation, - circuit_lib, child_pb_graph_node, - CIRCUIT_PB_PORT_CLOCK, physical_mode); + add_module_pb_graph_port_interc( + module_manager, pb_module, memory_modules, memory_instances, + device_annotation, circuit_lib, child_pb_graph_node, + CIRCUIT_PB_PORT_CLOCK, physical_mode, group_config_block, verbose); } } } @@ -892,7 +897,7 @@ static void rec_build_logical_tile_modules( const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, t_pb_graph_node* physical_pb_graph_node, - const bool& verbose) { + const bool& group_config_block, const bool& verbose) { /* Check cur_pb_graph_node*/ VTR_ASSERT(nullptr != physical_pb_graph_node); @@ -913,7 +918,7 @@ static void rec_build_logical_tile_modules( sram_orgz_type, sram_model, &(physical_pb_graph_node ->child_pb_graph_nodes[physical_mode->index][ipb][0]), - verbose); + group_config_block, verbose); } } @@ -921,7 +926,8 @@ static void rec_build_logical_tile_modules( if (true == is_primitive_pb_type(physical_pb_type)) { build_primitive_block_module(module_manager, decoder_lib, device_annotation, circuit_lib, sram_orgz_type, sram_model, - physical_pb_graph_node, verbose); + physical_pb_graph_node, group_config_block, + verbose); /* Finish for primitive node, return */ return; } @@ -1003,7 +1009,8 @@ static void rec_build_logical_tile_modules( */ add_module_pb_graph_interc(module_manager, pb_module, memory_modules, memory_instances, device_annotation, circuit_lib, - physical_pb_graph_node, physical_mode->index); + physical_pb_graph_node, physical_mode->index, + group_config_block, verbose); /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), @@ -1253,15 +1260,13 @@ static void build_physical_tile_module( * - Only one module for each CLB (FILL_TYPE) * - Only one module for each heterogeneous block ****************************************************************************/ -void build_grid_modules(ModuleManager& module_manager, - DecoderLibrary& decoder_lib, - const DeviceContext& device_ctx, - const VprDeviceAnnotation& device_annotation, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, - const bool& duplicate_grid_pin, const bool& verbose) { +void build_grid_modules( + ModuleManager& module_manager, DecoderLibrary& decoder_lib, + const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, + const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, const bool& duplicate_grid_pin, + const bool& group_config_block, const bool& verbose) { /* Start time count */ vtr::ScopedStartFinishTimer timer("Build grid modules"); @@ -1284,7 +1289,8 @@ void build_grid_modules(ModuleManager& module_manager, } rec_build_logical_tile_modules( module_manager, decoder_lib, device_annotation, circuit_lib, mux_lib, - sram_orgz_type, sram_model, logical_tile.pb_graph_head, verbose); + sram_orgz_type, sram_model, logical_tile.pb_graph_head, + group_config_block, verbose); } VTR_LOG("Done\n"); diff --git a/openfpga/src/fabric/build_grid_modules.h b/openfpga/src/fabric/build_grid_modules.h index 61c0963c3..94ac49dc2 100644 --- a/openfpga/src/fabric/build_grid_modules.h +++ b/openfpga/src/fabric/build_grid_modules.h @@ -17,15 +17,13 @@ /* begin namespace openfpga */ namespace openfpga { -void build_grid_modules(ModuleManager& module_manager, - DecoderLibrary& decoder_lib, - const DeviceContext& device_ctx, - const VprDeviceAnnotation& device_annotation, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, - const bool& duplicate_grid_pin, const bool& verbose); +void build_grid_modules( + ModuleManager& module_manager, DecoderLibrary& decoder_lib, + const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, + const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, const bool& duplicate_grid_pin, + const bool& group_config_block, const bool& verbose); } /* end namespace openfpga */ From 2f079c7b922b21725b57f3a43ee9c11de35efcf7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 31 Jul 2023 17:33:09 -0700 Subject: [PATCH 02/41] [doc] update for newly added option --- .../openfpga_shell/openfpga_commands/setup_commands.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst index 7aaa2f7e6..7bb03b27d 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -260,6 +260,10 @@ build_fabric .. warning:: This option does not support ``--duplicate_grid_pin``! .. warning:: This option requires ``--compress_routing`` to be enabled! + + .. option:: --group_config_block + + Group configuration memory blocks under each CLB/SB/CB etc. into a centralized configuration memory blocks. When disabled, the configuration memory blocks are placed in a distributed way under CLB/SB/CB etc. For example, each programming resource, e.g., LUT, has a dedicated configuration memory block, being placed in the same module. .. option:: --duplicate_grid_pin From 23643f3fb19af8cb758e0d49e2103dc11dec5227 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 31 Jul 2023 22:57:26 -0700 Subject: [PATCH 03/41] [core] developing the physical memory block builder --- libs/libarchopenfpga/src/circuit_types.h | 13 ++++--- .../src/openfpga_reserved_words.h | 2 + openfpga/src/base/openfpga_naming.cpp | 36 ++++++++++++++++-- openfpga/src/base/openfpga_naming.h | 6 ++- openfpga/src/fabric/build_device_module.cpp | 6 +-- openfpga/src/fabric/build_grid_modules.cpp | 38 +++++++++++-------- openfpga/src/fabric/build_memory_modules.cpp | 6 ++- openfpga/src/fabric/build_memory_modules.h | 3 +- openfpga/src/fabric/build_routing_modules.cpp | 10 ++++- openfpga/src/fabric/build_routing_modules.h | 8 +++- openfpga/src/fabric/module_manager.cpp | 29 ++++++++------ openfpga/src/fabric/module_manager.h | 21 +++++++--- openfpga/src/utils/memory_utils.cpp | 6 +++ openfpga/src/utils/module_manager_utils.cpp | 2 + 14 files changed, 135 insertions(+), 51 deletions(-) diff --git a/libs/libarchopenfpga/src/circuit_types.h b/libs/libarchopenfpga/src/circuit_types.h index 35000d80e..58107a0cc 100644 --- a/libs/libarchopenfpga/src/circuit_types.h +++ b/libs/libarchopenfpga/src/circuit_types.h @@ -129,10 +129,12 @@ constexpr std::array /******************************************************************** * Types of configuration protocol - * 1. configurable memories are organized and accessed as standalone elements - * 2. configurable memories are organized and accessed by a scan-chain - * 3. configurable memories are organized and accessed by memory bank - * 4. configurable memories are organized and accessed by frames + * - configurable memories are organized and accessed as standalone elements + * - configurable memories are organized and accessed by a scan-chain + * - configurable memories are organized and accessed by quicklogic memory bank + * - configurable memories are organized and accessed by memory bank + * - configurable memories are organized and accessed by frames + * - configurable memories are organized and accessed by feedthrough. Currently, this is only for internal use only */ enum e_config_protocol_type { CONFIG_MEM_STANDALONE, @@ -140,11 +142,12 @@ enum e_config_protocol_type { CONFIG_MEM_MEMORY_BANK, CONFIG_MEM_QL_MEMORY_BANK, CONFIG_MEM_FRAME_BASED, + CONFIG_MEM_FEEDTHROUGH, NUM_CONFIG_PROTOCOL_TYPES }; constexpr std::array CONFIG_PROTOCOL_TYPE_STRING = {{"standalone", "scan_chain", "memory_bank", - "ql_memory_bank", "frame_based"}}; + "ql_memory_bank", "frame_based", "feedthrough"}}; #endif diff --git a/libs/libopenfpgautil/src/openfpga_reserved_words.h b/libs/libopenfpgautil/src/openfpga_reserved_words.h index f06ed9c66..5774db1cf 100644 --- a/libs/libopenfpgautil/src/openfpga_reserved_words.h +++ b/libs/libopenfpgautil/src/openfpga_reserved_words.h @@ -41,6 +41,8 @@ constexpr const char* GRID_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* SWITCH_BLOCK_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* MEMORY_MODULE_POSTFIX = "_mem"; +constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME = "feedthrough_mem_in"; +constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME = "feedthrough_mem_inb"; constexpr const char* MEMORY_BL_PORT_NAME = "bl"; constexpr const char* MEMORY_WL_PORT_NAME = "wl"; constexpr const char* MEMORY_WLR_PORT_NAME = "wlr"; diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 784f51685..ba9677dcc 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -225,12 +225,18 @@ std::string generate_segment_wire_mid_output_name( /********************************************************************* * Generate the module name for a memory sub-circuit + * If this is a module just to feed through memory lines, use a special name ********************************************************************/ std::string generate_memory_module_name(const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model, const CircuitModelId& sram_model, - const std::string& postfix) { - return std::string(circuit_lib.model_name(circuit_model) + "_" + + const std::string& postfix, + const bool& feedthrough_memory) { + std::string mid_name; + if (feedthrough_memory) { + mid_name = "feedthrough_" + } + return std::string(circuit_lib.model_name(circuit_model) + "_" + mid_name + circuit_lib.model_name(sram_model) + postfix); } @@ -734,6 +740,26 @@ std::string generate_sram_port_name( std::string port_name; switch (sram_orgz_type) { + case CONFIG_MEM_FEEDTHROUGH: + /* Two types of ports are available: + * (1) BL indicates the mem port + * (2) BLB indicates the inverted mem port + * + * mem mem_inv + * [0] [0] + * | | + * v v + * +----------------+ + * | Virtual Mem | + * +----------------+ + */ + if (CIRCUIT_MODEL_PORT_BL == port_type) { + port_name = std::string(MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME); + } else { + VTR_ASSERT(CIRCUIT_MODEL_PORT_BLB == port_type); + port_name = std::string(MEMEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME); + } + break; case CONFIG_MEM_SCAN_CHAIN: /* Two types of ports are available: * (1) Head of a chain of Configuration-chain Flip-Flops (CCFFs), enabled @@ -1159,8 +1185,10 @@ std::string generate_pb_mux_instance_name(const std::string& prefix, ********************************************************************/ std::string generate_pb_memory_instance_name(const std::string& prefix, t_pb_graph_pin* pb_graph_pin, - const std::string& postfix) { - std::string instance_name(prefix); + const std::string& postfix, + const bool& feedthrough_memory) { + std::string mid_name = feedthrough_memory ? "virtual_" : ""; + std::string instance_name(mid_name + prefix); instance_name += std::string(pb_graph_pin->parent_node->pb_type->name); if (IN_PORT == pb_graph_pin->port->type) { diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index 389e4c951..e6eb59aa4 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -68,7 +68,8 @@ std::string generate_segment_wire_mid_output_name( std::string generate_memory_module_name(const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model, const CircuitModelId& sram_model, - const std::string& postfix); + const std::string& postfix, + const bool& feedthrough_memory = false); std::string generate_routing_block_netlist_name(const std::string& prefix, const size_t& block_id, @@ -144,7 +145,8 @@ std::string generate_pb_mux_instance_name(const std::string& prefix, std::string generate_pb_memory_instance_name(const std::string& prefix, t_pb_graph_pin* pb_graph_pin, - const std::string& postfix); + const std::string& postfix, + const bool& feedthrough_memory = false); std::string generate_grid_port_name(const size_t& width, const size_t& height, const int& subtile_index, diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 3b05142a1..ebf9a7a05 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -78,7 +78,7 @@ int build_device_module_graph( /* Build memory modules */ build_memory_modules(module_manager, decoder_lib, openfpga_ctx.mux_lib(), openfpga_ctx.arch().circuit_lib, - openfpga_ctx.arch().config_protocol.type()); + openfpga_ctx.arch().config_protocol.type(), group_config_block); /* Build grid and programmable block modules */ build_grid_modules(module_manager, decoder_lib, vpr_device_ctx, @@ -92,14 +92,14 @@ int build_device_module_graph( module_manager, decoder_lib, vpr_device_ctx, openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), openfpga_ctx.arch().circuit_lib, - openfpga_ctx.arch().config_protocol.type(), sram_model, verbose); + openfpga_ctx.arch().config_protocol.type(), sram_model, group_config_block, verbose); } else { VTR_ASSERT_SAFE(false == compress_routing); build_flatten_routing_modules( module_manager, decoder_lib, vpr_device_ctx, openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), openfpga_ctx.arch().circuit_lib, - openfpga_ctx.arch().config_protocol.type(), sram_model, verbose); + openfpga_ctx.arch().config_protocol.type(), sram_model, group_config_block, verbose); } /* Build tile modules if defined */ diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 19ea1a9f1..719777cfb 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -310,11 +310,12 @@ static void build_primitive_block_module( } /* Regular (independent) SRAM ports */ + e_config_protocol_type mem_module_type = group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; size_t num_config_bits = - find_circuit_num_config_bits(sram_orgz_type, circuit_lib, primitive_model); + find_circuit_num_config_bits(mem_module_type, circuit_lib, primitive_model); if (0 < num_config_bits) { add_sram_ports_to_module_manager(module_manager, primitive_module, - circuit_lib, sram_model, sram_orgz_type, + circuit_lib, sram_model, mem_module_type, num_config_bits); } @@ -335,7 +336,7 @@ static void build_primitive_block_module( /* Add the associated memory module as a child of primitive module */ std::string memory_module_name = generate_memory_module_name(circuit_lib, primitive_model, sram_model, - std::string(MEMORY_MODULE_POSTFIX)); + std::string(MEMORY_MODULE_POSTFIX), group_config_block); ModuleId memory_module = module_manager.find_module(memory_module_name); /* If there is no memory module required, we can skip the assocated net @@ -356,7 +357,7 @@ static void build_primitive_block_module( memory_module, memory_instance_id, circuit_lib, primitive_model); /* Record memory-related information */ module_manager.add_configurable_child(primitive_module, memory_module, - memory_instance_id); + memory_instance_id, group_config_block); } /* Add all the nets to connect configuration ports from memory module to @@ -638,12 +639,12 @@ static void add_module_pb_graph_pin_interc( * generation to modules */ std::string mux_mem_instance_name = generate_pb_memory_instance_name( - GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string("")); + GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), group_config_block); module_manager.set_child_instance_name( pb_module, mux_mem_module, mux_mem_instance, mux_mem_instance_name); /* Add this MUX as a configurable child to the pb_module */ module_manager.add_configurable_child(pb_module, mux_mem_module, - mux_mem_instance); + mux_mem_instance, group_config_block); /* Add nets to connect SRAM ports of the MUX to the SRAM port of memory * module */ @@ -955,6 +956,8 @@ static void rec_build_logical_tile_modules( std::vector memory_modules; std::vector memory_instances; + e_config_protocol mem_module_type = group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; + /* Add all the child Verilog modules as instances */ for (int ichild = 0; ichild < physical_mode->num_pb_type_children; ++ichild) { /* Get the name and module id for this child pb_type */ @@ -997,9 +1000,9 @@ static void rec_build_logical_tile_modules( */ if (0 < find_module_num_config_bits(module_manager, child_pb_module, circuit_lib, sram_model, - sram_orgz_type)) { + mem_module_type)) { module_manager.add_configurable_child(pb_module, child_pb_module, - child_instance_id); + child_instance_id, group_config_block); } } } @@ -1046,10 +1049,10 @@ static void rec_build_logical_tile_modules( */ size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, pb_module, circuit_lib, sram_model, sram_orgz_type); + module_manager, pb_module, circuit_lib, sram_model, mem_module_type); if (0 < module_num_config_bits) { add_sram_ports_to_module_manager(module_manager, pb_module, circuit_lib, - sram_model, sram_orgz_type, + sram_model, mem_module_type, module_num_config_bits); } @@ -1059,7 +1062,7 @@ static void rec_build_logical_tile_modules( */ if (0 < module_manager.configurable_children(pb_module).size()) { add_module_nets_memory_config_bus(module_manager, decoder_lib, pb_module, - sram_orgz_type, + mem_module_type, circuit_lib.design_tech_type(sram_model)); } @@ -1081,6 +1084,7 @@ static void build_physical_tile_module( const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, t_physical_tile_type_ptr phy_block_type, const e_side& border_side, const bool& duplicate_grid_pin, + const bool& group_config_block, const bool& verbose) { /* Create a Module for the top-level physical block, and add to module manager */ @@ -1133,13 +1137,17 @@ static void build_physical_tile_module( */ if (0 < find_module_num_config_bits(module_manager, pb_module, circuit_lib, sram_model, - sram_orgz_type)) { + group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type)) { + /* Only add logical configurable children here. Since we will add a physical memory block at this level */ module_manager.add_configurable_child(grid_module, pb_module, - pb_instance_id); + pb_instance_id, true); } } } + /* TODO: Add a physical memory block */ + add_physical_memory_module(module_manager, grid_module); + /* Add grid ports(pins) to the module */ if (false == duplicate_grid_pin) { /* Default way to add these ports by following the definition in pb_types */ @@ -1320,13 +1328,13 @@ void build_grid_modules( build_physical_tile_module(module_manager, decoder_lib, device_annotation, circuit_lib, sram_orgz_type, sram_model, &physical_tile, - io_type_side, duplicate_grid_pin, verbose); + io_type_side, duplicate_grid_pin, group_config_block, verbose); } } else { /* For CLB and heterogenenous blocks */ build_physical_tile_module(module_manager, decoder_lib, device_annotation, circuit_lib, sram_orgz_type, sram_model, - &physical_tile, NUM_SIDES, duplicate_grid_pin, + &physical_tile, NUM_SIDES, duplicate_grid_pin, group_config_block, verbose); } } diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 9ce406ca6..8b15b2acf 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1005,12 +1005,14 @@ static void build_mux_memory_module( * memory modules. * Take another example, the memory circuit can implement the scan-chain or * memory-bank organization for the memories. + * If we need feedthrough memory blocks, build the memory modules which contain only feedthrough wires ********************************************************************/ void build_memory_modules(ModuleManager& module_manager, DecoderLibrary& arch_decoder_lib, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const bool& require_feedthrough_memory) { vtr::ScopedStartFinishTimer timer("Build memory modules"); /* Create the memory circuits for the multiplexer */ @@ -1027,6 +1029,7 @@ void build_memory_modules(ModuleManager& module_manager, /* Create a Verilog module for the memories used by the multiplexer */ build_mux_memory_module(module_manager, arch_decoder_lib, circuit_lib, sram_orgz_type, mux_model, mux_graph); + /* TODO: Create feedthrough memory module */ } /* Create the memory circuits for non-MUX circuit models. @@ -1063,6 +1066,7 @@ void build_memory_modules(ModuleManager& module_manager, /* Create a Verilog module for the memories used by the circuit model */ build_memory_module(module_manager, arch_decoder_lib, circuit_lib, sram_orgz_type, module_name, sram_models[0], num_mems); + /* TODO: Create feedthrough memory module */ } } diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index 5eeaf25e8..1fbbe1caf 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -26,7 +26,8 @@ void build_memory_modules(ModuleManager& module_manager, DecoderLibrary& arch_decoder_lib, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const e_config_protocol_type& sram_orgz_type); + const e_config_protocol_type& sram_orgz_type, + const bool& require_feedthrough_memory); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index f57ccce29..a392d8058 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -461,6 +461,8 @@ static void build_switch_block_module( } } + /* TODO: Build a physical memory block */ + /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), * we just need to find all the global ports from the child modules and build @@ -1038,7 +1040,9 @@ void build_flatten_routing_modules( const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, const bool& verbose) { + const CircuitModelId& sram_model, + const bool& group_config_block, + const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build routing modules..."); vtr::Point sb_range = device_rr_gsb.get_gsb_range(); @@ -1082,7 +1086,9 @@ void build_unique_routing_modules( const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, const bool& verbose) { + const CircuitModelId& sram_model, + const bool& group_config_block, + const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build unique routing modules..."); /* Build unique switch block modules */ diff --git a/openfpga/src/fabric/build_routing_modules.h b/openfpga/src/fabric/build_routing_modules.h index 59274bc11..e83f1a35d 100644 --- a/openfpga/src/fabric/build_routing_modules.h +++ b/openfpga/src/fabric/build_routing_modules.h @@ -24,14 +24,18 @@ void build_flatten_routing_modules( const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, const bool& verbose); + const CircuitModelId& sram_model, + const bool& group_config_block, + const bool& verbose); void build_unique_routing_modules( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, const bool& verbose); + const CircuitModelId& sram_model, + const bool& group_config_block, + const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 9b35670cd..4bc00cbb8 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -903,6 +903,7 @@ void ModuleManager::set_child_instance_name(const ModuleId& parent_module, void ModuleManager::add_configurable_child(const ModuleId& parent_module, const ModuleId& child_module, const size_t& child_instance, + const bool& logical_only, const vtr::Point coord) { /* Validate the id of both parent and child modules */ VTR_ASSERT(valid_module_id(parent_module)); @@ -910,29 +911,35 @@ void ModuleManager::add_configurable_child(const ModuleId& parent_module, /* Ensure that the instance id is in range */ VTR_ASSERT(child_instance < num_instance(parent_module, child_module)); - configurable_children_[parent_module].push_back(child_module); - configurable_child_instances_[parent_module].push_back(child_instance); - configurable_child_regions_[parent_module].push_back( + logical_configurable_children_[parent_module].push_back(child_module); + logical_configurable_child_instances_[parent_module].push_back(child_instance); + logical_configurable_child_regions_[parent_module].push_back( ConfigRegionId::INVALID()); - configurable_child_coordinates_[parent_module].push_back(coord); + logical_configurable_child_coordinates_[parent_module].push_back(coord); + + if (!logical_only) { + physical_configurable_children_[parent_module].push_back(child_module); + physical_configurable_child_instances_[parent_module].push_back(child_instance); + physical_configurable_child_parents_[parent_module].push_back(parent_module); + } } -void ModuleManager::reserve_configurable_child(const ModuleId& parent_module, +void ModuleManager::reserve_logical_configurable_child(const ModuleId& parent_module, const size_t& num_children) { VTR_ASSERT(valid_module_id(parent_module)); /* Do reserve when the number of children is larger than current size of lists */ - if (num_children > configurable_children_[parent_module].size()) { - configurable_children_[parent_module].reserve(num_children); + if (num_children > logical_configurable_children_[parent_module].size()) { + logical_configurable_children_[parent_module].reserve(num_children); } - if (num_children > configurable_child_instances_[parent_module].size()) { - configurable_child_instances_[parent_module].reserve(num_children); + if (num_children > logical_configurable_child_instances_[parent_module].size()) { + logical_configurable_child_instances_[parent_module].reserve(num_children); } if (num_children > configurable_child_regions_[parent_module].size()) { - configurable_child_regions_[parent_module].reserve(num_children); + logical_configurable_child_regions_[parent_module].reserve(num_children); } if (num_children > configurable_child_coordinates_[parent_module].size()) { - configurable_child_coordinates_[parent_module].reserve(num_children); + logical_configurable_child_coordinates_[parent_module].reserve(num_children); } } diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index ce936add9..1eced58bc 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -358,6 +358,7 @@ class ModuleManager { void add_configurable_child( const ModuleId& module, const ModuleId& child_module, const size_t& child_instance, + const bool& logical_only, const vtr::Point coord = vtr::Point(-1, -1)); /* Reserved a number of configurable children for memory efficiency */ void reserve_configurable_child(const ModuleId& module, @@ -513,21 +514,31 @@ class ModuleManager { * protocol is organized which should be made by users/designers */ vtr::vector> - configurable_children_; /* Child modules with configurable memory bits that + logical_configurable_children_; /* Child modules with configurable memory bits that this module contain */ vtr::vector> - configurable_child_instances_; /* Instances of child modules with + logical_configurable_child_instances_; /* Instances of child modules with configurable memory bits that this module contain */ vtr::vector> - configurable_child_regions_; /* Instances of child modules with configurable + logical_configurable_child_regions_; /* Instances of child modules with configurable memory bits that this module contain */ vtr::vector>> - configurable_child_coordinates_; /* Relative coorindates of child modules + logical_configurable_child_coordinates_; /* Relative coorindates of child modules with configurable memory bits that this module contain */ + vtr::vector> + physical_configurable_children_; /* Child modules with configurable memory bits that + this module contain */ + vtr::vector> + physical_configurable_child_instances_; /* Instances of child modules with + configurable memory bits that this module + contain */ + vtr::vector> + physical_configurable_child_parents_; /* Parent modules with configurable memory bits that + this module contain */ - /* Configurable regions to group the configurable children + /* Configurable regions to group the physical configurable children * Note: * - Each child can only be added a group */ diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index df144e669..4cf44b163 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -342,6 +342,11 @@ std::vector generate_sram_port_names( std::vector model_port_types; switch (sram_orgz_type) { + case CONFIG_MEM_FEEDTHROUGH: + /* Feed through wires are all inputs */ + model_port_types.push_back(CIRCUIT_MODEL_PORT_BL); /* Indicate mem port */ + model_port_types.push_back(CIRCUIT_MODEL_PORT_BLB); /* Indicate mem_inv port */ + break; case CONFIG_MEM_SCAN_CHAIN: model_port_types.push_back(CIRCUIT_MODEL_PORT_INPUT); model_port_types.push_back(CIRCUIT_MODEL_PORT_OUTPUT); @@ -400,6 +405,7 @@ size_t generate_sram_port_size(const e_config_protocol_type sram_orgz_type, size_t sram_port_size = num_config_bits; switch (sram_orgz_type) { + case CONFIG_MEM_FEEDTHROUGH: case CONFIG_MEM_STANDALONE: break; case CONFIG_MEM_SCAN_CHAIN: diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 37bbdd2cb..767e542fb 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -342,6 +342,7 @@ void add_sram_ports_to_module_manager( /* Add ports to the module manager */ switch (sram_orgz_type) { + case CONFIG_MEM_FEEDTHROUGH: case CONFIG_MEM_STANDALONE: case CONFIG_MEM_QL_MEMORY_BANK: case CONFIG_MEM_MEMORY_BANK: { @@ -1730,6 +1731,7 @@ static void add_module_nets_cmos_memory_config_bus( module_manager, parent_module, sram_orgz_type); break; } + case CONFIG_MEM_FEEDTHROUGH: case CONFIG_MEM_STANDALONE: case CONFIG_MEM_QL_MEMORY_BANK: case CONFIG_MEM_MEMORY_BANK: From 53050b94abe0820519cb51c55e9822eba69783cc Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 1 Aug 2023 17:50:03 -0700 Subject: [PATCH 04/41] [core] developing memory group modules in grid modules --- .../src/openfpga_reserved_words.h | 1 + openfpga/src/base/openfpga_naming.cpp | 7 + openfpga/src/base/openfpga_naming.h | 2 + openfpga/src/fabric/build_grid_modules.cpp | 103 +++++- openfpga/src/fabric/build_memory_modules.cpp | 303 +++++++++++++++++- openfpga/src/fabric/build_memory_modules.h | 8 + openfpga/src/utils/memory_utils.cpp | 16 + openfpga/src/utils/memory_utils.h | 7 + openfpga/src/utils/module_manager_utils.cpp | 1 + 9 files changed, 441 insertions(+), 7 deletions(-) diff --git a/libs/libopenfpgautil/src/openfpga_reserved_words.h b/libs/libopenfpgautil/src/openfpga_reserved_words.h index 5774db1cf..cd6b6148f 100644 --- a/libs/libopenfpgautil/src/openfpga_reserved_words.h +++ b/libs/libopenfpgautil/src/openfpga_reserved_words.h @@ -41,6 +41,7 @@ constexpr const char* GRID_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* SWITCH_BLOCK_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* MEMORY_MODULE_POSTFIX = "_mem"; +constexpr const char* MEMORY_FEEDTHROUGH_MODULE_POSTFIX = "_feedthrough_mem"; constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME = "feedthrough_mem_in"; constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME = "feedthrough_mem_inb"; constexpr const char* MEMORY_BL_PORT_NAME = "bl"; diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index ba9677dcc..ec8b3bf25 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -528,6 +528,13 @@ std::string generate_tile_module_netlist_name(const std::string& block_name, return block_name + postfix; } +/********************************************************************* + * Generate the module name of a physical memory module + **********************************************************************/ +std::string generate_physical_memory_module_name(const size_t& mem_size) { + return std::string("physical_config_mem_size") + std::to_string(mem_size); +} + /********************************************************************* * Generate the module name for a connection block with a given coordinate *********************************************************************/ diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index e6eb59aa4..a474c7200 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -119,6 +119,8 @@ std::string generate_tile_module_port_name(const std::string& prefix, std::string generate_tile_module_netlist_name(const std::string& block_name, const std::string& postfix); +std::string generate_physical_memory_module_name(const size_t& mem_size); + std::string generate_sb_mux_instance_name(const std::string& prefix, const e_side& sb_side, const size_t& track_id, diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 719777cfb..a9814d0f0 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -1069,6 +1069,103 @@ static void rec_build_logical_tile_modules( VTR_LOGV(verbose, "Done\n"); } +/***************************************************************************** + * This function creates a physical memory module and add it the current module + * The following tasks will be accomplished: + * - Traverse all the logical configurable children in the module tree, starting from the current module + * - Build a list of the leaf logical configurable children and count the total memory sizes, the memory size for each physical memory submodule. Note that the physical memory submodule should be cached already in each leaf logical configurable children + * - Get the physical memory module required by each leaf logical configurable child + * - Create a dedicated module name for the physical memory (check if already exists, if yes, skip creating a new module) + * - Instanciate the module + * - Built nets. Note that only the output ports of the physical memory block is required, since they should drive the dedicated memory ports of logical configurable children + *****************************************************************************/ +static int add_physical_memory_module(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, + const ModuleId& curr_module, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model) { + int status = CMD_EXEC_SUCCESS; + + std::vector required_phy_mem_modules; + status = rec_find_physical_memory_children(static_cast(module_manager), curr_module, required_phy_mem_modules); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + size_t module_num_config_bits = + find_module_num_config_bits_from_child_modules( + module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH); + std::string phy_mem_module_name = generate_physical_memory_module_name(module_num_config_bits); + ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); + if (!module_manager.valid_module_id(phy_mem_module)) { + status = build_memory_group_module(module_manager, decode_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules); + } + if (status != CMD_EXEC_SUCCESS) { + VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + phy_mem_module = module_manager.find_module(phy_mem_module_name); + if (!module_manager.valid_module_id(phy_mem_module)) { + VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + /* Add the physical memory module to the current module */ + size_t phy_mem_instance = module_manager.num_instance(curr_module, phy_mem_module); + module_manager.add_child_module(curr_module, phy_mem_module, false); + + /* Register in the physical configurable children list */ + module_manager.add_physical_configurable_child(curr_module, phy_mem_module, phy_mem_instance, curr_module); + + /* Build nets between the data output of the physical memory module and the outputs of the logical configurable children */ + size_t curr_mem_pin_index = 0; + std::map mem2mem_port_map; + mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); + mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); + for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { + for (CircuitPortType port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { + std::string src_port_name = mem2mem_port_map[port_type]; + std::string des_port_name = + generate_sram_port_name(CONFIG_MEM_FEEDTHROUGH, port_type); + /* Try to find these ports in the module manager */ + ModulePortId src_port_id = + module_manager.find_module_port(phy_mem_module, src_port_name); + if (!module_manager.valid_module_port_id(phy_mem_module, src_port_id)) { + return CMD_EXEC_FATAL_ERROR; + } + BasicPort src_port = + module_manager.module_port(phy_mem_module, src_port_id); + + ModuleId des_module = module_manager.logical_configurable_children(curr_module)[ichild]; + size_t des_instance = module_manager.logical_configurable_child_instances(curr_module)[ichild]; + ModulePortId des_port_id = + module_manager.find_module_port(des_module, des_port_name); + if (!module_manager.valid_module_port_id(des_module, des_port_id)) { + return CMD_EXEC_FATAL_ERROR; + } + BasicPort des_port = + module_manager.module_port(des_module, des_port_id); + /* Build nets */ + for (size_t ipin = 0; ipin < des_port.pins().size(); ++ipin) { + /* Create a net and add source and sink to it */ + ModuleNetId net = create_module_source_pin_net( + module_manager, curr_module, phy_mem_module, phy_mem_instance, + src_port_id, src_port.pins()[cur_mem_pin_index]); + if (module_manager.valid_module_net_id(curr_module, net)) { + return CMD_EXEC_FATAL_ERROR; + } + /* Add net sink */ + module_manager.add_module_net_sink(curr_module, net, des_module, + des_instance, des_port_id, + des_port.pins()[ipin]); + curr_mem_pin_index++; + } + } + } + + return status; +} + /***************************************************************************** * This function will create a Verilog file and print out a Verilog netlist * for a type of physical block @@ -1140,13 +1237,15 @@ static void build_physical_tile_module( group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type)) { /* Only add logical configurable children here. Since we will add a physical memory block at this level */ module_manager.add_configurable_child(grid_module, pb_module, - pb_instance_id, true); + pb_instance_id, group_config_block); } } } /* TODO: Add a physical memory block */ - add_physical_memory_module(module_manager, grid_module); + if (group_config_block) { + add_physical_memory_module(module_manager, decoder_lib, grid_module, circuit_lib, sram_orgz_type, sram_model); + } /* Add grid ports(pins) to the module */ if (false == duplicate_grid_pin) { diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 8b15b2acf..1f7cb9bae 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -6,8 +6,7 @@ #include #include #include - -/* Headers from vtrutil library */ +#include "command_exit_codes.h" #include "build_decoder_modules.h" #include "build_memory_modules.h" #include "circuit_library_utils.h" @@ -932,6 +931,79 @@ static void build_memory_module(ModuleManager& module_manager, } } +/********************************************************************* + * Generate Verilog modules for the feedthrough memories that are used + * by a circuit model + * mem_out mem_outb + * | | + * v v + * +------------------------------------+ + * | | + * | | + * | | + * +------------------------------------+ + * | | + * | mem_in | mem_inb + * v v + * +------------------------------------+ + * | Multiplexer Configuration port | + * + ********************************************************************/ +static int build_feedthrough_memory_module(ModuleManager& module_manager, + const std::string& module_name, + const size_t& num_mems) { + /* Create a module and add to the module manager */ + ModuleId mem_module = module_manager.add_module(module_name); + if (!module_manager.valid_module_id(mem_module)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Label module usage */ + module_manager.set_module_usage(mem_module, ModuleManager::MODULE_CONFIG); + + /* Add module ports */ + /* Input: memory inputs */ + BasicPort in_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME), num_mems); + ModulePortId mem_in_port = module_manager.add_port( + mem_module, in_port, ModuleManager::MODULE_INPUT_PORT); + BasicPort inb_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME), num_mems); + ModulePortId mem_inb_port = module_manager.add_port( + mem_module, inb_port, ModuleManager::MODULE_INPUT_PORT); + + /* Add each output port */ + BasicPort out_port(std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME), num_mems); + ModulePortId mem_out_port = module_manager.add_port( + mem_module, out_port, ModuleManager::MODULE_OUTPUT_PORT); + BasicPort outb_port(std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME), num_mems); + ModulePortId mem_outb_port = module_manager.add_port( + mem_module, outb_port, ModuleManager::MODULE_OUTPUT_PORT); + + /* Build feedthrough nets */ + for (size_t pin_id = 0; pin_id < in_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(mem_module); + if (!module_manager.valid_module_net_id(mem_module, net)) { + return CMD_EXEC_FATAL_ERROR; + } + module_manager.add_module_net_source(mem_module, net, mem_module, 0, + mem_in_port, in_port.pins()[pin_id]); + module_manager.add_module_net_sink( + mem_module, net, mem_module, 0, mem_out_port, out_port.pins()[pin_id]); + } + for (size_t pin_id = 0; pin_id < inb_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(mem_module); + if (!module_manager.valid_module_net_id(mem_module, net)) { + return CMD_EXEC_FATAL_ERROR; + } + module_manager.add_module_net_source(mem_module, net, mem_module, 0, + mem_inb_port, inb_port.pins()[pin_id]); + module_manager.add_module_net_sink( + mem_module, net, mem_module, 0, mem_outb_port, outb_port.pins()[pin_id]); + } + + return CMD_EXEC_SUCCESS; +} + + /********************************************************************* * Generate Verilog modules for the memories that are used * by multiplexers @@ -990,6 +1062,62 @@ static void build_mux_memory_module( } } +/********************************************************************* + * Generate Verilog modules for the feedthrough memories that are used + * by multiplexers + * SRAM ports as feedthrough (driven by physical memory blocks) + * | | | | + * v v ... v v + * +----------------+ + * | Memory Module | + * +----------------+ + * | | ... | | + * v v v v SRAM ports of multiplexer + * +---------------------+ + * in--->| Multiplexer Module |---> out + * +---------------------+ + ********************************************************************/ +static int build_mux_feedthrough_memory_module( + ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, const CircuitModelId& mux_model, + const MuxGraph& mux_graph) { + int status = CMD_EXEC_SUCCESS; + /* Find the actual number of configuration bits, based on the mux graph + * Due to the use of local decoders inside mux, this may be + */ + size_t num_config_bits = + find_mux_num_config_bits(circuit_lib, mux_model, mux_graph, sram_orgz_type); + /* Multiplexers built with different technology is in different organization + */ + switch (circuit_lib.design_tech_type(mux_model)) { + case CIRCUIT_MODEL_DESIGN_CMOS: { + /* Generate module name */ + std::string module_name = generate_mux_subckt_name( + circuit_lib, mux_model, + find_mux_num_datapath_inputs(circuit_lib, mux_model, + mux_graph.num_inputs()), + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + + status = build_feedthrough_memory_module(module_manager, module_name, num_config_bits); + break; + } + case CIRCUIT_MODEL_DESIGN_RRAM: + /* We do not need a memory submodule for RRAM MUX, + * RRAM are embedded in the datapath + * TODO: generate local encoders for RRAM-based multiplexers here!!! + */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid design technology of multiplexer '%s'\n", + circuit_lib.model_name(mux_model).c_str()); + exit(1); + } + return status; +} + + /********************************************************************* * Build modules for * the memories that are affiliated to multiplexers and other programmable @@ -1007,12 +1135,13 @@ static void build_mux_memory_module( * memory-bank organization for the memories. * If we need feedthrough memory blocks, build the memory modules which contain only feedthrough wires ********************************************************************/ -void build_memory_modules(ModuleManager& module_manager, +int build_memory_modules(ModuleManager& module_manager, DecoderLibrary& arch_decoder_lib, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const bool& require_feedthrough_memory) { + int status = CMD_EXEC_SUCCESS; vtr::ScopedStartFinishTimer timer("Build memory modules"); /* Create the memory circuits for the multiplexer */ @@ -1029,7 +1158,14 @@ void build_memory_modules(ModuleManager& module_manager, /* Create a Verilog module for the memories used by the multiplexer */ build_mux_memory_module(module_manager, arch_decoder_lib, circuit_lib, sram_orgz_type, mux_model, mux_graph); - /* TODO: Create feedthrough memory module */ + /* Create feedthrough memory module */ + if (require_feedthrough_memory) { + status = build_mux_feedthrough_memory_module(module_manager, circuit_lib, + sram_orgz_type, mux_model, mux_graph); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } } /* Create the memory circuits for non-MUX circuit models. @@ -1066,8 +1202,165 @@ void build_memory_modules(ModuleManager& module_manager, /* Create a Verilog module for the memories used by the circuit model */ build_memory_module(module_manager, arch_decoder_lib, circuit_lib, sram_orgz_type, module_name, sram_models[0], num_mems); - /* TODO: Create feedthrough memory module */ + /* Create feedthrough memory module */ + if (require_feedthrough_memory) { + status = build_feedthrough_memory_module(module_manager, module_name, num_mems); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + } + return status; +} + +/********************************************************************* + * Add module nets to connect an output port of a configuration-chain + * memory module to an output port of its child module + * Restriction: this function is really designed for memory modules + * 1. It assumes that output port name of child module is the same as memory + *module + * 2. It assumes exact pin-to-pin mapping: + * j-th pin of output port of the i-th child module is wired to the j + i*W + *-th pin of output port of the memory module, where W is the size of port + * 3. It assumes fixed port name for output ports + ********************************************************************/ +void add_module_output_nets_to_memory_group_module( + ModuleManager& module_manager, const ModuleId& mem_module, + const std::string& mem_module_output_name, const ModuleId& child_module, + const size_t& output_pin_start_index, const size_t& child_instance) { + /* Wire inputs of parent module to inputs of child modules */ + ModulePortId src_port_id = module_manager.find_module_port( + child_module, mem_module_output_name); + ModulePortId sink_port_id = + module_manager.find_module_port(mem_module, mem_module_output_name); + for (size_t pin_id = 0; + pin_id < + module_manager.module_port(child_module, src_port_id).pins().size(); + ++pin_id) { + ModuleNetId net = module_manager.create_module_net(mem_module); + /* Source pin is shifted by the number of memories */ + size_t src_pin_id = + module_manager.module_port(child_module, src_port_id).pins()[pin_id]; + /* Source node of the input net is the input of memory module */ + module_manager.add_module_net_source( + mem_module, net, child_module, child_instance, src_port_id, src_pin_id); + /* Sink node of the input net is the input of sram module */ + size_t sink_pin_id = + output_pin_start_index + + module_manager.module_port(mem_module, sink_port_id).pins()[pin_id]; + module_manager.add_module_net_sink(mem_module, net, mem_module, 0, + sink_port_id, sink_pin_id); } } +/********************************************************************* + * Build a grouped memory module based on existing memory modules + * - Create the module + * - Add dedicated instance + * - Add ports + * - Add nets + ********************************************************************/ +int build_memory_group_module(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const std::string& module_name, + const CircuitModelId& sram_model, + const std::vector& child_modules, + const size_t& num_mems) { + ModuleId mem_module = module_manager.add_module(module_name); + if (!module_manager.valid_module_id(mem_module)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Add output ports */ + std::string out_port_name = generate_configurable_memory_data_out_name(); + BasicPort out_port(out_port_name, num_mems); + module_manager.add_port(mem_module, out_port, + ModuleManager::MODULE_OUTPUT_PORT); + + std::string outb_port_name = generate_configurable_memory_inverted_data_out_name(); + BasicPort outb_port(outb_port_name, num_mems); + module_manager.add_port(mem_module, outb_port, + ModuleManager::MODULE_OUTPUT_PORT); + + /* Add nets between child module outputs and memory modules */ + size_t mem_out_pin_start_index = 0; + size_t mem_outb_pin_start_index = 0; + for (size_t ichild = 0; ichild < child_modules.size(); ++ichild) { + ModuleId child_module = child_modules[ichild]; + size_t child_instance = module_manager.num_instance(mem_module, child_module); + module_manager.add_child_module(mem_module, child_module, false); + module_manager.add_configurable_child(mem_module, child_module, child_instance, false); + /* Wire outputs of child module to outputs of parent module */ + add_module_output_nets_to_memory_group_module( + module_manager, mem_module, out_port_name, + child_module, mem_out_pin_start_index, child_instance); + add_module_output_nets_to_memory_group_module( + module_manager, mem_module, outb_port_name, + child_module, mem_outb_pin_start_index, child_instance); + /* Update pin counter */ + ModulePortId child_out_port_id = module_manager.find_module_port(child_module, out_port_name); + mem_out_pin_start_index += module_manager.module_port(child_module, child_out_port_id).get_width(); + + ModulePortId child_outb_port_id = module_manager.find_module_port(child_module, outb_port_name); + mem_outb_pin_start_index += module_manager.module_port(child_module, child_outb_port_id).get_width(); + } + /* Check pin counter */ + VTR_ASSERT(mem_out_pin_start_index == num_mems && mem_outb_pin_start_index == num_mems); + + /* Add global ports to the pb_module: + * This is a much easier job after adding sub modules (instances), + * we just need to find all the global ports from the child modules and build + * a list of it + */ + add_module_global_ports_from_child_modules(module_manager, mem_module); + + /* Count GPIO ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a + * list of it + */ + add_module_gpio_ports_from_child_modules(module_manager, mem_module); + + /* Count shared SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a + * list of it + */ + size_t module_num_shared_config_bits = + find_module_num_shared_config_bits_from_child_modules(module_manager, + mem_module); + if (0 < module_num_shared_config_bits) { + add_reserved_sram_ports_to_module_manager(module_manager, mem_module, + module_num_shared_config_bits); + } + + /* Count SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a + * list of it + */ + size_t module_num_config_bits = + find_module_num_config_bits_from_child_modules( + module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type); + if (0 < module_num_config_bits) { + add_sram_ports_to_module_manager(module_manager, mem_module, circuit_lib, + sram_model, sram_orgz_type, + module_num_config_bits); + } + + /* Add module nets to connect memory cells inside + * This is a one-shot addition that covers all the memory modules in this pb + * module! + */ + if (0 < module_manager.configurable_children(mem_module).size()) { + add_module_nets_memory_config_bus(module_manager, decoder_lib, mem_module, + sram_orgz_type, + circuit_lib.design_tech_type(sram_model)); + } + + return CMD_EXEC_SUCCESS; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index 1fbbe1caf..6431eaf9a 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -29,6 +29,14 @@ void build_memory_modules(ModuleManager& module_manager, const e_config_protocol_type& sram_orgz_type, const bool& require_feedthrough_memory); +int build_memory_group_module(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const std::string& module_name, + const CircuitModelId& sram_model, + const std::vector& child_modules); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index 4cf44b163..05dbf439b 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -496,4 +496,20 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( return num_child_to_skip; } +int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector& physical_memory_children) { + if (module_manager.logical_configurable_children(curr_module).empty()) { + return CMD_EXEC_SUCCESS; + } + for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { + ModuleId logical_child = module_manager.logical_configurable_children(curr_module)[ichild]; + if (module_manager.logical_configurable_children(logical_child).empty()) { + /* This is a leaf node, get the physical memory module */ + physical_memory_children.push_back(module_manager.physical_configurable_children(curr_module)[ichild]); + } else { + rec_find_physical_memory_children(module_manager, logical_child, physical_memory_children); + } + } + return CMD_EXEC_SUCCESS; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/utils/memory_utils.h b/openfpga/src/utils/memory_utils.h index 59cf08953..5097c962d 100644 --- a/openfpga/src/utils/memory_utils.h +++ b/openfpga/src/utils/memory_utils.h @@ -53,6 +53,13 @@ size_t generate_pb_sram_port_size(const e_config_protocol_type sram_orgz_type, size_t estimate_num_configurable_children_to_skip_by_config_protocol( const ConfigProtocol& config_protocol, size_t curr_region_num_config_child); +/** + * @brief Find the physical memory child modules with a given root module + * This function will walk through the module tree in a recursive way until reaching the leaf node (which require configurable memories) + * Return a list of modules + */ +int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector& physical_memory_children); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 767e542fb..ca384d99d 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -2370,6 +2370,7 @@ size_t find_module_num_config_bits_from_child_modules( size_t num_config_bits = 0; switch (sram_orgz_type) { + case CONFIG_MEM_FEEDTHROUGH: case CONFIG_MEM_STANDALONE: case CONFIG_MEM_SCAN_CHAIN: case CONFIG_MEM_QL_MEMORY_BANK: From 470ab84489ed680e77e4a3c3e618a4c63d38f41a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 1 Aug 2023 22:57:22 -0700 Subject: [PATCH 05/41] [core] developing group config block support for routing module --- openfpga/src/base/openfpga_naming.cpp | 12 +++- openfpga/src/base/openfpga_naming.h | 6 +- openfpga/src/fabric/build_routing_modules.cpp | 67 +++++++++++++------ 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index ec8b3bf25..2a9d4a410 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -1098,8 +1098,12 @@ std::string generate_sb_mux_instance_name(const std::string& prefix, std::string generate_sb_memory_instance_name(const std::string& prefix, const e_side& sb_side, const size_t& track_id, - const std::string& postfix) { + const std::string& postfix, + const bool& logical_memory) { std::string instance_name(prefix); + if (logical_memory) { + instance_name = std::string("virtual_") + instance_name; + } instance_name += SideManager(sb_side).to_string(); instance_name += std::string("_track_") + std::to_string(track_id); instance_name += postfix; @@ -1136,8 +1140,12 @@ std::string generate_cb_mux_instance_name(const std::string& prefix, std::string generate_cb_memory_instance_name(const std::string& prefix, const e_side& cb_side, const size_t& pin_id, - const std::string& postfix) { + const std::string& postfix, + const bool& logical_memory) { std::string instance_name(prefix); + if (logical_memory) { + instance_name = std::string("virtual_") + instance_name; + } instance_name += SideManager(cb_side).to_string(); instance_name += std::string("_ipin_") + std::to_string(pin_id); diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index a474c7200..a4417bb84 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -129,7 +129,8 @@ std::string generate_sb_mux_instance_name(const std::string& prefix, std::string generate_sb_memory_instance_name(const std::string& prefix, const e_side& sb_side, const size_t& track_id, - const std::string& postfix); + const std::string& postfix, + const bool& logical_memory = false); std::string generate_cb_mux_instance_name(const std::string& prefix, const e_side& cb_side, @@ -139,7 +140,8 @@ std::string generate_cb_mux_instance_name(const std::string& prefix, std::string generate_cb_memory_instance_name(const std::string& prefix, const e_side& cb_side, const size_t& pin_id, - const std::string& postfix); + const std::string& postfix, + const bool& logical_memory = false); std::string generate_pb_mux_instance_name(const std::string& prefix, t_pb_graph_pin* pb_graph_pin, diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index a392d8058..a0b7cde15 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -108,7 +108,8 @@ static void build_switch_block_mux_module( const CircuitLibrary& circuit_lib, const e_side& chan_side, const size_t& chan_node_id, const RRNodeId& cur_rr_node, const std::vector& driver_rr_nodes, const RRSwitchId& switch_index, - const std::map& input_port_to_module_nets) { + const std::map& input_port_to_module_nets, + const bool& group_config_block) { /* Check current rr_node is CHANX or CHANY*/ VTR_ASSERT((CHANX == rr_graph.node_type(cur_rr_node)) || (CHANY == rr_graph.node_type(cur_rr_node))); @@ -214,6 +215,11 @@ static void build_switch_block_mux_module( std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); + if (group_config_block) { + mem_module_name = + generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + } ModuleId mem_module = module_manager.find_module(mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); @@ -224,7 +230,7 @@ static void build_switch_block_mux_module( * modules */ std::string mem_instance_name = generate_sb_memory_instance_name( - SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string("")); + SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string(""), group_config_block); module_manager.set_child_instance_name(sb_module, mem_module, mem_instance_id, mem_instance_name); @@ -234,7 +240,7 @@ static void build_switch_block_mux_module( module_manager, sb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ - module_manager.add_configurable_child(sb_module, mem_module, mem_instance_id); + module_manager.add_configurable_child(sb_module, mem_module, mem_instance_id, group_config_block); } /********************************************************************* @@ -248,7 +254,8 @@ static void build_switch_block_interc_modules( const RRGraphView& rr_graph, const RRGSB& rr_gsb, const CircuitLibrary& circuit_lib, const e_side& chan_side, const size_t& chan_node_id, - const std::map& input_port_to_module_nets) { + const std::map& input_port_to_module_nets, + const bool& group_config_block) { std::vector driver_rr_nodes; /* Get the node */ @@ -284,7 +291,7 @@ static void build_switch_block_interc_modules( build_switch_block_mux_module( module_manager, sb_module, device_annotation, grids, rr_graph, rr_gsb, circuit_lib, chan_side, chan_node_id, cur_rr_node, driver_rr_nodes, - driver_switches[0], input_port_to_module_nets); + driver_switches[0], input_port_to_module_nets, group_config_block); } /*Nothing should be done else*/ } @@ -354,7 +361,8 @@ static void build_switch_block_module( const VprDeviceAnnotation& device_annotation, const DeviceGrid& grids, const RRGraphView& rr_graph, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, const RRGSB& rr_gsb, const bool& verbose) { + const CircuitModelId& sram_model, const RRGSB& rr_gsb, + const bool& group_config_block, const bool& verbose) { /* Create a Module of Switch Block and add to module manager */ vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); ModuleId sb_module = module_manager.add_module( @@ -456,12 +464,15 @@ static void build_switch_block_module( build_switch_block_interc_modules( module_manager, sb_module, device_annotation, grids, rr_graph, rr_gsb, circuit_lib, side_manager.get_side(), itrack, - input_port_to_module_nets); + input_port_to_module_nets, group_config_block); } } } - /* TODO: Build a physical memory block */ + /* Build a physical memory block */ + if (group_config_block) { + add_physical_memory_module(module_manager, decoder_lib, sb_module, circuit_lib, sram_orgz_type, sram_model); + } /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), @@ -588,7 +599,8 @@ static void build_connection_block_mux_module( const RRGraphView& rr_graph, const RRGSB& rr_gsb, const t_rr_type& cb_type, const CircuitLibrary& circuit_lib, const e_side& cb_ipin_side, const size_t& ipin_index, - const std::map& input_port_to_module_nets) { + const std::map& input_port_to_module_nets, + const bool& group_config_block) { const RRNodeId& cur_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, ipin_index); /* Check current rr_node is an input pin of a CLB */ VTR_ASSERT(IPIN == rr_graph.node_type(cur_rr_node)); @@ -699,6 +711,11 @@ static void build_connection_block_mux_module( std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); + if (group_config_block) { + mem_module_name = + generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + } ModuleId mem_module = module_manager.find_module(mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); @@ -713,7 +730,7 @@ static void build_connection_block_mux_module( CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, get_rr_graph_single_node_side( rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, ipin_index)), - ipin_index, std::string("")); + ipin_index, std::string(""), group_config_block); module_manager.set_child_instance_name(cb_module, mem_module, mem_instance_id, mem_instance_name); @@ -723,7 +740,7 @@ static void build_connection_block_mux_module( module_manager, cb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ - module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id); + module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id, group_config_block); } /******************************************************************** @@ -739,7 +756,8 @@ static void build_connection_block_interc_modules( const RRGraphView& rr_graph, const RRGSB& rr_gsb, const t_rr_type& cb_type, const CircuitLibrary& circuit_lib, const e_side& cb_ipin_side, const size_t& ipin_index, - const std::map& input_port_to_module_nets) { + const std::map& input_port_to_module_nets, + const bool& group_config_block) { std::vector driver_rr_edges = rr_gsb.get_ipin_node_in_edges(rr_graph, cb_ipin_side, ipin_index); @@ -756,7 +774,7 @@ static void build_connection_block_interc_modules( build_connection_block_mux_module( module_manager, cb_module, device_annotation, grids, rr_graph, rr_gsb, cb_type, circuit_lib, cb_ipin_side, ipin_index, - input_port_to_module_nets); + input_port_to_module_nets, group_config_block); } /*Nothing should be done else*/ } @@ -821,7 +839,9 @@ static void build_connection_block_module( const RRGraphView& rr_graph, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, const RRGSB& rr_gsb, - const t_rr_type& cb_type, const bool& verbose) { + const t_rr_type& cb_type, + const bool& group_config_block, + const bool& verbose) { /* Create the netlist */ vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); @@ -943,10 +963,15 @@ static void build_connection_block_module( ++inode) { build_connection_block_interc_modules( module_manager, cb_module, device_annotation, grids, rr_graph, rr_gsb, - cb_type, circuit_lib, cb_ipin_side, inode, input_port_to_module_nets); + cb_type, circuit_lib, cb_ipin_side, inode, input_port_to_module_nets, group_config_block); } } + /* Build a physical memory block */ + if (group_config_block) { + add_physical_memory_module(module_manager, decoder_lib, sb_module, circuit_lib, sram_orgz_type, sram_model); + } + /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), * we just need to find all the global ports from the child modules and build @@ -1057,17 +1082,17 @@ void build_flatten_routing_modules( build_switch_block_module(module_manager, decoder_lib, device_annotation, device_ctx.grid, device_ctx.rr_graph, circuit_lib, sram_orgz_type, sram_model, rr_gsb, - verbose); + group_config_block, verbose); } } build_flatten_connection_block_modules( module_manager, decoder_lib, device_ctx, device_annotation, device_rr_gsb, - circuit_lib, sram_orgz_type, sram_model, CHANX, verbose); + circuit_lib, sram_orgz_type, sram_model, CHANX, group_config_block, verbose); build_flatten_connection_block_modules( module_manager, decoder_lib, device_ctx, device_annotation, device_rr_gsb, - circuit_lib, sram_orgz_type, sram_model, CHANY, verbose); + circuit_lib, sram_orgz_type, sram_model, CHANY, group_config_block, verbose); } /******************************************************************** @@ -1097,7 +1122,7 @@ void build_unique_routing_modules( build_switch_block_module(module_manager, decoder_lib, device_annotation, device_ctx.grid, device_ctx.rr_graph, circuit_lib, sram_orgz_type, sram_model, unique_mirror, - verbose); + group_config_block, verbose); } /* Build unique X-direction connection block modules */ @@ -1108,7 +1133,7 @@ void build_unique_routing_modules( build_connection_block_module( module_manager, decoder_lib, device_annotation, device_ctx.grid, device_ctx.rr_graph, circuit_lib, sram_orgz_type, sram_model, - unique_mirror, CHANX, verbose); + unique_mirror, CHANX, group_config_block, verbose); } /* Build unique X-direction connection block modules */ @@ -1119,7 +1144,7 @@ void build_unique_routing_modules( build_connection_block_module( module_manager, decoder_lib, device_annotation, device_ctx.grid, device_ctx.rr_graph, circuit_lib, sram_orgz_type, sram_model, - unique_mirror, CHANY, verbose); + unique_mirror, CHANY, group_config_block, verbose); } } From c05f12ac118d1578d8ef1f399530aa25fe3bbf47 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 2 Aug 2023 12:24:16 -0700 Subject: [PATCH 06/41] [core] sync up logical-to-physical configurable child mapping after physical memory build-up --- openfpga/src/fabric/build_grid_modules.cpp | 107 ++------------- openfpga/src/fabric/build_memory_modules.cpp | 114 ++++++++++++++++ openfpga/src/fabric/build_memory_modules.h | 7 + openfpga/src/fabric/build_routing_modules.cpp | 19 +++ openfpga/src/fabric/module_manager.cpp | 127 ++++++++++++++---- openfpga/src/fabric/module_manager.h | 31 ++++- openfpga/src/utils/memory_utils.cpp | 23 ++++ openfpga/src/utils/memory_utils.h | 7 + 8 files changed, 308 insertions(+), 127 deletions(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index a9814d0f0..6001b00e4 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -15,6 +15,7 @@ #include "build_grid_module_duplicated_pins.h" #include "build_grid_module_utils.h" #include "build_grid_modules.h" +#include "build_memory_modules.h" #include "circuit_library_utils.h" #include "module_manager_utils.h" #include "openfpga_interconnect_types.h" @@ -356,8 +357,17 @@ static void build_primitive_block_module( module_manager, primitive_module, logic_module, logic_instance_id, memory_module, memory_instance_id, circuit_lib, primitive_model); /* Record memory-related information */ + size_t config_child_id = module_manager.num_configurable_children(primitive_module); module_manager.add_configurable_child(primitive_module, memory_module, memory_instance_id, group_config_block); + /* For logical memory, define the physical memory here */ + if (group_config_block) { + std::string physical_memory_module_name = + generate_memory_module_name(circuit_lib, primitive_model, sram_model, + std::string(MEMORY_MODULE_POSTFIX), false); + ModuleId physical_memory_module = module_manager.find_module(physical_memory_module_name); + module_manager.set_physical_configurable_child(primitive_module, config_child_id, physical_memory_module); + } } /* Add all the nets to connect configuration ports from memory module to @@ -1069,103 +1079,6 @@ static void rec_build_logical_tile_modules( VTR_LOGV(verbose, "Done\n"); } -/***************************************************************************** - * This function creates a physical memory module and add it the current module - * The following tasks will be accomplished: - * - Traverse all the logical configurable children in the module tree, starting from the current module - * - Build a list of the leaf logical configurable children and count the total memory sizes, the memory size for each physical memory submodule. Note that the physical memory submodule should be cached already in each leaf logical configurable children - * - Get the physical memory module required by each leaf logical configurable child - * - Create a dedicated module name for the physical memory (check if already exists, if yes, skip creating a new module) - * - Instanciate the module - * - Built nets. Note that only the output ports of the physical memory block is required, since they should drive the dedicated memory ports of logical configurable children - *****************************************************************************/ -static int add_physical_memory_module(ModuleManager& module_manager, - DecoderLibrary& decoder_lib, - const ModuleId& curr_module, - const CircuitLibrary& circuit_lib, - const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model) { - int status = CMD_EXEC_SUCCESS; - - std::vector required_phy_mem_modules; - status = rec_find_physical_memory_children(static_cast(module_manager), curr_module, required_phy_mem_modules); - if (status != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; - } - - size_t module_num_config_bits = - find_module_num_config_bits_from_child_modules( - module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH); - std::string phy_mem_module_name = generate_physical_memory_module_name(module_num_config_bits); - ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); - if (!module_manager.valid_module_id(phy_mem_module)) { - status = build_memory_group_module(module_manager, decode_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules); - } - if (status != CMD_EXEC_SUCCESS) { - VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); - return CMD_EXEC_FATAL_ERROR; - } - phy_mem_module = module_manager.find_module(phy_mem_module_name); - if (!module_manager.valid_module_id(phy_mem_module)) { - VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); - return CMD_EXEC_FATAL_ERROR; - } - /* Add the physical memory module to the current module */ - size_t phy_mem_instance = module_manager.num_instance(curr_module, phy_mem_module); - module_manager.add_child_module(curr_module, phy_mem_module, false); - - /* Register in the physical configurable children list */ - module_manager.add_physical_configurable_child(curr_module, phy_mem_module, phy_mem_instance, curr_module); - - /* Build nets between the data output of the physical memory module and the outputs of the logical configurable children */ - size_t curr_mem_pin_index = 0; - std::map mem2mem_port_map; - mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); - mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); - for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { - for (CircuitPortType port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { - std::string src_port_name = mem2mem_port_map[port_type]; - std::string des_port_name = - generate_sram_port_name(CONFIG_MEM_FEEDTHROUGH, port_type); - /* Try to find these ports in the module manager */ - ModulePortId src_port_id = - module_manager.find_module_port(phy_mem_module, src_port_name); - if (!module_manager.valid_module_port_id(phy_mem_module, src_port_id)) { - return CMD_EXEC_FATAL_ERROR; - } - BasicPort src_port = - module_manager.module_port(phy_mem_module, src_port_id); - - ModuleId des_module = module_manager.logical_configurable_children(curr_module)[ichild]; - size_t des_instance = module_manager.logical_configurable_child_instances(curr_module)[ichild]; - ModulePortId des_port_id = - module_manager.find_module_port(des_module, des_port_name); - if (!module_manager.valid_module_port_id(des_module, des_port_id)) { - return CMD_EXEC_FATAL_ERROR; - } - BasicPort des_port = - module_manager.module_port(des_module, des_port_id); - /* Build nets */ - for (size_t ipin = 0; ipin < des_port.pins().size(); ++ipin) { - /* Create a net and add source and sink to it */ - ModuleNetId net = create_module_source_pin_net( - module_manager, curr_module, phy_mem_module, phy_mem_instance, - src_port_id, src_port.pins()[cur_mem_pin_index]); - if (module_manager.valid_module_net_id(curr_module, net)) { - return CMD_EXEC_FATAL_ERROR; - } - /* Add net sink */ - module_manager.add_module_net_sink(curr_module, net, des_module, - des_instance, des_port_id, - des_port.pins()[ipin]); - curr_mem_pin_index++; - } - } - } - - return status; -} - /***************************************************************************** * This function will create a Verilog file and print out a Verilog netlist * for a type of physical block diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 1f7cb9bae..51821ffa7 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -15,6 +15,7 @@ #include "module_manager_utils.h" #include "mux_graph.h" #include "mux_utils.h" +#include "memory_utils.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" #include "vtr_assert.h" @@ -1363,4 +1364,117 @@ int build_memory_group_module(ModuleManager& module_manager, return CMD_EXEC_SUCCESS; } +/***************************************************************************** + * This function creates a physical memory module and add it the current module + * The following tasks will be accomplished: + * - Traverse all the logical configurable children in the module tree, starting from the current module + * - Build a list of the leaf logical configurable children and count the total memory sizes, the memory size for each physical memory submodule. Note that the physical memory submodule should be cached already in each leaf logical configurable children + * - Get the physical memory module required by each leaf logical configurable child + * - Create a dedicated module name for the physical memory (check if already exists, if yes, skip creating a new module) + * - Instanciate the module + * - Built nets. Note that only the output ports of the physical memory block is required, since they should drive the dedicated memory ports of logical configurable children + *****************************************************************************/ +int add_physical_memory_module(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, + const ModuleId& curr_module, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model) { + int status = CMD_EXEC_SUCCESS; + + std::vector required_phy_mem_modules; + status = rec_find_physical_memory_children(static_cast(module_manager), curr_module, required_phy_mem_modules); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + size_t module_num_config_bits = + find_module_num_config_bits_from_child_modules( + module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH); + std::string phy_mem_module_name = generate_physical_memory_module_name(module_num_config_bits); + ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); + if (!module_manager.valid_module_id(phy_mem_module)) { + status = build_memory_group_module(module_manager, decode_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules); + } + if (status != CMD_EXEC_SUCCESS) { + VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + phy_mem_module = module_manager.find_module(phy_mem_module_name); + if (!module_manager.valid_module_id(phy_mem_module)) { + VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + /* Add the physical memory module to the current module */ + size_t phy_mem_instance = module_manager.num_instance(curr_module, phy_mem_module); + module_manager.add_child_module(curr_module, phy_mem_module, false); + + /* Register in the physical configurable children list */ + module_manager.add_physical_configurable_child(curr_module, phy_mem_module, phy_mem_instance, curr_module); + + /* Build nets between the data output of the physical memory module and the outputs of the logical configurable children */ + size_t curr_mem_pin_index = 0; + std::map mem2mem_port_map; + mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); + mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); + for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { + for (CircuitPortType port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { + std::string src_port_name = mem2mem_port_map[port_type]; + std::string des_port_name = + generate_sram_port_name(CONFIG_MEM_FEEDTHROUGH, port_type); + /* Try to find these ports in the module manager */ + ModulePortId src_port_id = + module_manager.find_module_port(phy_mem_module, src_port_name); + if (!module_manager.valid_module_port_id(phy_mem_module, src_port_id)) { + return CMD_EXEC_FATAL_ERROR; + } + BasicPort src_port = + module_manager.module_port(phy_mem_module, src_port_id); + + ModuleId des_module = module_manager.logical_configurable_children(curr_module)[ichild]; + size_t des_instance = module_manager.logical_configurable_child_instances(curr_module)[ichild]; + ModulePortId des_port_id = + module_manager.find_module_port(des_module, des_port_name); + if (!module_manager.valid_module_port_id(des_module, des_port_id)) { + return CMD_EXEC_FATAL_ERROR; + } + BasicPort des_port = + module_manager.module_port(des_module, des_port_id); + /* Build nets */ + for (size_t ipin = 0; ipin < des_port.pins().size(); ++ipin) { + /* Create a net and add source and sink to it */ + ModuleNetId net = create_module_source_pin_net( + module_manager, curr_module, phy_mem_module, phy_mem_instance, + src_port_id, src_port.pins()[cur_mem_pin_index]); + if (module_manager.valid_module_net_id(curr_module, net)) { + return CMD_EXEC_FATAL_ERROR; + } + /* Add net sink */ + module_manager.add_module_net_sink(curr_module, net, des_module, + des_instance, des_port_id, + des_port.pins()[ipin]); + curr_mem_pin_index++; + } + } + } + + /* TODO: Recursively update the logical configurable child with the physical memory module parent and its instance id */ + std::map logical_mem_child_inst_count; + status = rec_update_logical_memory_children_with_physical_mapping(module_manager, curr_module, phy_mem_module, logical_mem_child_inst_count); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + /* Sanity check */ + std::map required_mem_child_inst_count; + for (ModuleId curr_module : module_manager.child_modules(phy_mem_module)) { + if (logical_mem_child_inst_count[curr_module] != module_manager.num_instance(phy_mem_module, curr_module)) { + VTR_LOG_ERROR("Expect the %lu instances of module '%s' under its parent '%s' while only updated %lu during logical-to-physical configurable child mapping sync-up!\n", module_manager.num_instance(phy_mem_module, curr_module), module_manager.module_name(curr_module).c_str(), module_manager.module_name(phy_mem_module).c_str(), logical_mem_child_inst_count[curr_module]); + return CMD_EXEC_FATAL_ERROR; + } + } + + return status; +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index 6431eaf9a..116e3c8e6 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -37,6 +37,13 @@ int build_memory_group_module(ModuleManager& module_manager, const CircuitModelId& sram_model, const std::vector& child_modules); +int add_physical_memory_module(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, + const ModuleId& curr_module, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index a0b7cde15..b889f86fe 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -17,6 +17,7 @@ #include "build_module_graph_utils.h" #include "build_routing_module_utils.h" #include "build_routing_modules.h" +#include "build_memory_modules.h" #include "module_manager_utils.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" @@ -240,7 +241,16 @@ static void build_switch_block_mux_module( module_manager, sb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ + size_t config_child_id = module_manager.num_configurable_children(sb_module); module_manager.add_configurable_child(sb_module, mem_module, mem_instance_id, group_config_block); + /* For logical memory, define the physical memory here */ + if (group_config_block) { + std::string physical_mem_module_name = + generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, + std::string(MEMORY_MODULE_POSTFIX)); + ModuleId physical_mem_module = module_manager.find_module(physical_mem_module_name); + module_manager.set_physical_configurable_child(sb_module, config_child_id, physical_mem_module); + } } /********************************************************************* @@ -740,7 +750,16 @@ static void build_connection_block_mux_module( module_manager, cb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ + size_t config_child_id = module_manager.num_configurable_children(cb_module); module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id, group_config_block); + /* For logical memory, define the physical memory here */ + if (group_config_block) { + std::string physical_mem_module_name = + generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, + std::string(MEMORY_MODULE_POSTFIX)); + ModuleId physical_mem_module = module_manager.find_module(physical_mem_module_name); + module_manager.set_physical_configurable_child(cb_module, config_child_id, physical_mem_module); + } } /******************************************************************** diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 4bc00cbb8..53239eaf0 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -77,29 +77,56 @@ std::vector ModuleManager::child_module_instances( } /* Find all the configurable child modules under a parent module */ -std::vector ModuleManager::configurable_children( +std::vector ModuleManager::logical_configurable_children( const ModuleId& parent_module) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); - return configurable_children_[parent_module]; + return logical_configurable_children_[parent_module]; } /* Find all the instances of configurable child modules under a parent module */ -std::vector ModuleManager::configurable_child_instances( +std::vector ModuleManager::logical_configurable_child_instances( const ModuleId& parent_module) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); - return configurable_child_instances_[parent_module]; + return logical_configurable_child_instances_[parent_module]; } -std::vector> ModuleManager::configurable_child_coordinates( +std::vector> ModuleManager::logical_configurable_child_coordinates( const ModuleId& parent_module) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); - return configurable_child_coordinates_[parent_module]; + return logical_configurable_child_coordinates_[parent_module]; +} + +/* Find all the configurable child modules under a parent module */ +std::vector ModuleManager::physical_configurable_children( + const ModuleId& parent_module) const { + /* Validate the module_id */ + VTR_ASSERT(valid_module_id(parent_module)); + + return physical_configurable_children_[parent_module]; +} + +/* Find all the instances of configurable child modules under a parent module */ +std::vector ModuleManager::physical_configurable_child_instances( + const ModuleId& parent_module) const { + /* Validate the module_id */ + VTR_ASSERT(valid_module_id(parent_module)); + + return physical_configurable_child_instances_[parent_module]; +} + +/* Find all the instances of configurable child modules under a parent module */ +std::vector ModuleManager::physical_configurable_child_parents( + const ModuleId& parent_module) const { + /* Validate the module_id */ + VTR_ASSERT(valid_module_id(parent_module)); + + return physical_configurable_child_parents_[parent_module]; } /* Find all the configurable child modules under a parent module */ @@ -166,7 +193,7 @@ std::vector ModuleManager::region_configurable_children( for (const size_t& child_id : config_region_children_[parent_module][region]) { region_config_children.push_back( - configurable_children_[parent_module][child_id]); + logical_configurable_children_[parent_module][child_id]); } return region_config_children; @@ -185,7 +212,7 @@ std::vector ModuleManager::region_configurable_child_instances( for (const size_t& child_id : config_region_children_[parent_module][region]) { region_config_child_instances.push_back( - configurable_child_instances_[parent_module][child_id]); + logical_configurable_child_instances_[parent_module][child_id]); } return region_config_child_instances; @@ -205,7 +232,7 @@ ModuleManager::region_configurable_child_coordinates( for (const size_t& child_id : config_region_children_[parent_module][region]) { region_config_child_coordinates.push_back( - configurable_child_coordinates_[parent_module][child_id]); + logical_configurable_child_coordinates_[parent_module][child_id]); } return region_config_child_coordinates; @@ -376,6 +403,11 @@ size_t ModuleManager::instance_id(const ModuleId& parent_module, return size_t(-1); } +size_t ModuleManager::num_logical_configurable_children(const ModuleId& parent_module) const { + VTR_ASSERT(valid_module_id(parent_module)); + return logical_configurable_children_[parent_module].size(); +} + ModuleManager::e_module_port_type ModuleManager::port_type( const ModuleId& module, const ModulePortId& port) const { /* validate both module id and port id*/ @@ -654,10 +686,14 @@ ModuleId ModuleManager::add_module(const std::string& name) { children_.emplace_back(); num_child_instances_.emplace_back(); child_instance_names_.emplace_back(); - configurable_children_.emplace_back(); - configurable_child_instances_.emplace_back(); - configurable_child_regions_.emplace_back(); - configurable_child_coordinates_.emplace_back(); + logical_configurable_children_.emplace_back(); + logical_configurable_child_instances_.emplace_back(); + logical_configurable_child_regions_.emplace_back(); + logical_configurable_child_coordinates_.emplace_back(); + + physical_configurable_children_.emplace_back(); + physical_configurable_child_instances_.emplace_back(); + physical_configurable_child_parents_.emplace_back(); config_region_ids_.emplace_back(); config_region_children_.emplace_back(); @@ -921,10 +957,38 @@ void ModuleManager::add_configurable_child(const ModuleId& parent_module, physical_configurable_children_[parent_module].push_back(child_module); physical_configurable_child_instances_[parent_module].push_back(child_instance); physical_configurable_child_parents_[parent_module].push_back(parent_module); + } else { + physical_configurable_children_[parent_module].emplace_back(); + physical_configurable_child_instances_[parent_module].emplace_back(); + physical_configurable_child_parents_[parent_module].emplace_back(); } } -void ModuleManager::reserve_logical_configurable_child(const ModuleId& parent_module, +void ModuleManager::set_physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module) { + /* Sanity checks */ + VTR_ASSERT(valid_module_id(parent_module)); + VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); + /* Create the pair */ + physical_configurable_children_[parent_module][logical_child_id] = physical_child_module; +} + +void ModuleManager::set_physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance) { + /* Sanity checks */ + VTR_ASSERT(valid_module_id(parent_module)); + VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); + /* Create the pair */ + physical_configurable_child_instances_[parent_module][logical_child_id] = physical_child_instance; +} + +void ModuleManager::set_physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module) { + /* Sanity checks */ + VTR_ASSERT(valid_module_id(parent_module)); + VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); + /* Create the pair */ + physical_configurable_child_parents_[parent_module][logical_child_id] = physical_child_parent_module; +} + +void ModuleManager::reserve_configurable_child(const ModuleId& parent_module, const size_t& num_children) { VTR_ASSERT(valid_module_id(parent_module)); /* Do reserve when the number of children is larger than current size of lists @@ -935,12 +999,21 @@ void ModuleManager::reserve_logical_configurable_child(const ModuleId& parent_mo if (num_children > logical_configurable_child_instances_[parent_module].size()) { logical_configurable_child_instances_[parent_module].reserve(num_children); } - if (num_children > configurable_child_regions_[parent_module].size()) { + if (num_children > logical_configurable_child_regions_[parent_module].size()) { logical_configurable_child_regions_[parent_module].reserve(num_children); } - if (num_children > configurable_child_coordinates_[parent_module].size()) { + if (num_children > logical_configurable_child_coordinates_[parent_module].size()) { logical_configurable_child_coordinates_[parent_module].reserve(num_children); } + if (num_children > physical_configurable_children_[parent_module].size()) { + physical_configurable_children_[parent_module].reserve(num_children); + } + if (num_children > physical_configurable_child_instances_[parent_module].size()) { + physical_configurable_child_instances_[parent_module].reserve(num_children); + } + if (num_children > physical_configurable_child_parents_[parent_module].size()) { + physical_configurable_child_parents_[parent_module].reserve(num_children); + } } ConfigRegionId ModuleManager::add_config_region(const ModuleId& module) { @@ -968,23 +1041,23 @@ void ModuleManager::add_configurable_child_to_region( /* Ensure that the child module is in the configurable children list */ VTR_ASSERT(child_module == - configurable_children(parent_module)[config_child_id]); + logical_configurable_children(parent_module)[config_child_id]); VTR_ASSERT(child_instance == - configurable_child_instances(parent_module)[config_child_id]); + logical_configurable_child_instances(parent_module)[config_child_id]); /* If the child is already in another region, error out */ if ((true == valid_region_id( parent_module, - configurable_child_regions_[parent_module][config_child_id])) && + logical_configurable_child_regions_[parent_module][config_child_id])) && (config_region != - configurable_child_regions_[parent_module][config_child_id])) { + logical_configurable_child_regions_[parent_module][config_child_id])) { VTR_LOGF_ERROR( __FILE__, __LINE__, "Try to add a configurable child '%s[%lu]' to region '%lu' which is " "already added to another region '%lu'!\n", module_name(child_module).c_str(), child_instance, size_t(config_region), - size_t(configurable_child_regions_[parent_module][config_child_id])); + size_t(logical_configurable_child_regions_[parent_module][config_child_id])); exit(1); } @@ -1306,10 +1379,14 @@ ModuleId ModuleManager::create_wrapper_module( void ModuleManager::clear_configurable_children(const ModuleId& parent_module) { VTR_ASSERT(valid_module_id(parent_module)); - configurable_children_[parent_module].clear(); - configurable_child_instances_[parent_module].clear(); - configurable_child_regions_[parent_module].clear(); - configurable_child_coordinates_[parent_module].clear(); + logical_configurable_children_[parent_module].clear(); + logical_configurable_child_instances_[parent_module].clear(); + logical_configurable_child_regions_[parent_module].clear(); + logical_configurable_child_coordinates_[parent_module].clear(); + + parent_configurable_children_[parent_module].clear(); + parent_configurable_child_instances_[parent_module].clear(); + parent_configurable_child_parents_[parent_module].clear(); } void ModuleManager::clear_config_region(const ModuleId& parent_module) { diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 1eced58bc..0d4a5b2f3 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -166,15 +166,28 @@ class ModuleManager { std::vector child_module_instances( const ModuleId& parent_module, const ModuleId& child_module) const; /* Find all the configurable child modules under a parent module */ - std::vector configurable_children( + std::vector logical_configurable_children( const ModuleId& parent_module) const; /* Find all the instances of configurable child modules under a parent module */ - std::vector configurable_child_instances( + std::vector logical_configurable_child_instances( const ModuleId& parent_module) const; /* Find the coordindate of a configurable child module under a parent module */ - std::vector> configurable_child_coordinates( + std::vector> logical_configurable_child_coordinates( + const ModuleId& parent_module) const; + + /* Find all the configurable child modules under a parent module */ + std::vector physical_configurable_children( + const ModuleId& parent_module) const; + /* Find all the instances of configurable child modules under a parent module + */ + std::vector physical_configurable_child_instances( + const ModuleId& parent_module) const; + /* Find all the parent modules of physical configurable child modules under a parent module + * Note that a physical configurable child module may be at another module; Only the logical child module is under the current parent module + */ + std::vector physical_configurable_child_parents( const ModuleId& parent_module) const; /* Find all the I/O child modules under a parent module */ @@ -195,16 +208,17 @@ class ModuleManager { /* Find all the regions */ region_range regions(const ModuleId& module) const; /* Find all the configurable child modules under a region of a parent module + * Note that we use logical children here */ std::vector region_configurable_children( const ModuleId& parent_module, const ConfigRegionId& region) const; /* Find all the instances of configurable child modules under a region of a - * parent module */ + * parent module; Note that we use logical children here */ std::vector region_configurable_child_instances( const ModuleId& parent_module, const ConfigRegionId& region) const; /* Find all the coordinates of configurable child modules under a region of a - * parent module */ + * parent module; Note that we use logical children here */ std::vector> region_configurable_child_coordinates( const ModuleId& parent_module, const ConfigRegionId& region) const; @@ -238,6 +252,8 @@ class ModuleManager { size_t instance_id(const ModuleId& parent_module, const ModuleId& child_module, const std::string& instance_name) const; + /** @brief Count the number of logical configurable children */ + size_t num_logical_configurable_children(const ModuleId& parent_module) const; /* Find the type of a port */ ModuleManager::e_module_port_type port_type(const ModuleId& module, const ModulePortId& port) const; @@ -360,6 +376,11 @@ class ModuleManager { const size_t& child_instance, const bool& logical_only, const vtr::Point coord = vtr::Point(-1, -1)); + /** @brief Create a pair of mapping from a logical configurable child to a physical configurable child */ + void set_physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module); + /** @brief Create a pair of mapping from a logical configurable child to a physical configurable child */ + void set_physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance); + void set_physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module); /* Reserved a number of configurable children for memory efficiency */ void reserve_configurable_child(const ModuleId& module, const size_t& num_children); diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index 05dbf439b..7d3b6b23f 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -512,4 +512,27 @@ int rec_find_physical_memory_children(const ModuleManager& module_manager, const return CMD_EXEC_SUCCESS; } +int rec_update_logical_memory_children_with_physical_mapping(ModuleManager& module_manager, const ModuleId& curr_module, const ModuleId& phy_mem_module, std::map& logical_mem_child_inst_count) { + if (module_manager.logical_configurable_children(curr_module).empty()) { + return CMD_EXEC_SUCCESS; + } + for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { + ModuleId logical_child = module_manager.logical_configurable_children(curr_module)[ichild]; + if (module_manager.logical_configurable_children(logical_child).empty()) { + /* This is a leaf node, update its physical information */ + ModuleId phy_mem_submodule = module_manager.physical_configurable_children(curr_module)[ichild] + auto result = logical_mem_child_inst_count.find(phy_mem_submodule); + if (result == logical_mem_child_inst_count.end()) { + logical_mem_child_inst_count.find[phy_mem_submodule] = 0; + } + module_manager.set_physical_configurable_child_instance(curr_module, ichild, logical_mem_child_inst_count[phy_mem_submodule]); + module_manager.set_physical_configurable_child_parent_module(curr_module, ichild, phy_mem_module); + logical_mem_child_inst_count[phy_mem_submodule]++; + } else { + rec_find_physical_memory_children(module_manager, logical_child, physical_memory_children, logical_mem_child_inst_count); + } + } + return CMD_EXEC_SUCCESS; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/utils/memory_utils.h b/openfpga/src/utils/memory_utils.h index 5097c962d..7714242fa 100644 --- a/openfpga/src/utils/memory_utils.h +++ b/openfpga/src/utils/memory_utils.h @@ -60,6 +60,13 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( */ int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector& physical_memory_children); +/** + * @brief Update all the mappings between logical-to-physical memory children with a given root module + * This function will walk through the module tree in a recursive way until reaching the leaf node (which require configurable memories) + * Keep a scoreboard of instance number for checking. Note that when calling this the function, use an empty scoreboard! + */ +int rec_update_logical_memory_children_with_physical_mapping(ModuleManager& module_manager, const ModuleId& curr_module, const ModuleId& phy_mem_module, std::map& logical_mem_child_inst_count); + } /* end namespace openfpga */ #endif From 87f2822ef855f20bbf7998ec477ad1bcfec7e2a1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 2 Aug 2023 19:46:27 -0700 Subject: [PATCH 07/41] [core] working on logical and physical children --- openfpga/src/base/openfpga_naming.cpp | 20 +++++----- openfpga/src/base/openfpga_naming.h | 6 +-- .../base/openfpga_setup_command_template.h | 11 +++-- .../fabric/build_fpga_core_wrapper_module.cpp | 2 +- openfpga/src/fabric/build_grid_modules.cpp | 6 +-- openfpga/src/fabric/build_memory_modules.cpp | 4 +- openfpga/src/fabric/build_tile_modules.cpp | 2 +- .../src/fabric/build_top_module_memory.cpp | 40 ++++++++++--------- openfpga/src/fabric/fabric_key_writer.cpp | 10 ++--- openfpga/src/fabric/module_manager.cpp | 6 +-- openfpga/src/fabric/module_manager.h | 17 +++++++- .../fpga_bitstream/build_device_bitstream.cpp | 12 +++--- .../fpga_bitstream/build_fabric_bitstream.cpp | 20 +++++----- .../build_fabric_bitstream_memory_bank.cpp | 4 +- .../fpga_bitstream/build_grid_bitstream.cpp | 2 +- openfpga/src/utils/module_manager_utils.cpp | 4 +- 16 files changed, 91 insertions(+), 75 deletions(-) diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 2a9d4a410..9a8e64c5e 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -234,7 +234,7 @@ std::string generate_memory_module_name(const CircuitLibrary& circuit_lib, const bool& feedthrough_memory) { std::string mid_name; if (feedthrough_memory) { - mid_name = "feedthrough_" + mid_name = "feedthrough_"; } return std::string(circuit_lib.model_name(circuit_model) + "_" + mid_name + circuit_lib.model_name(sram_model) + postfix); @@ -531,8 +531,8 @@ std::string generate_tile_module_netlist_name(const std::string& block_name, /********************************************************************* * Generate the module name of a physical memory module **********************************************************************/ -std::string generate_physical_memory_module_name(const size_t& mem_size) { - return std::string("physical_config_mem_size") + std::to_string(mem_size); +std::string generate_physical_memory_module_name(const std::string& prefix, const size_t& mem_size) { + return prefix + std::string("_config_group_mem_size") + std::to_string(mem_size); } /********************************************************************* @@ -764,7 +764,7 @@ std::string generate_sram_port_name( port_name = std::string(MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME); } else { VTR_ASSERT(CIRCUIT_MODEL_PORT_BLB == port_type); - port_name = std::string(MEMEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME); + port_name = std::string(MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME); } break; case CONFIG_MEM_SCAN_CHAIN: @@ -1099,10 +1099,10 @@ std::string generate_sb_memory_instance_name(const std::string& prefix, const e_side& sb_side, const size_t& track_id, const std::string& postfix, - const bool& logical_memory) { + const bool& feedthrough_memory) { std::string instance_name(prefix); - if (logical_memory) { - instance_name = std::string("virtual_") + instance_name; + if (feedthrough_memory) { + instance_name = std::string("feedthrough_") + instance_name; } instance_name += SideManager(sb_side).to_string(); instance_name += std::string("_track_") + std::to_string(track_id); @@ -1141,10 +1141,10 @@ std::string generate_cb_memory_instance_name(const std::string& prefix, const e_side& cb_side, const size_t& pin_id, const std::string& postfix, - const bool& logical_memory) { + const bool& feedthrough_memory) { std::string instance_name(prefix); - if (logical_memory) { - instance_name = std::string("virtual_") + instance_name; + if (feedthrough_memory) { + instance_name = std::string("feedthrough_") + instance_name; } instance_name += SideManager(cb_side).to_string(); diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index a4417bb84..c6a84b3e3 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -119,7 +119,7 @@ std::string generate_tile_module_port_name(const std::string& prefix, std::string generate_tile_module_netlist_name(const std::string& block_name, const std::string& postfix); -std::string generate_physical_memory_module_name(const size_t& mem_size); +std::string generate_physical_memory_module_name(const std::string& prefix, const size_t& mem_size); std::string generate_sb_mux_instance_name(const std::string& prefix, const e_side& sb_side, @@ -130,7 +130,7 @@ std::string generate_sb_memory_instance_name(const std::string& prefix, const e_side& sb_side, const size_t& track_id, const std::string& postfix, - const bool& logical_memory = false); + const bool& feedthrough_memory = false); std::string generate_cb_mux_instance_name(const std::string& prefix, const e_side& cb_side, @@ -141,7 +141,7 @@ std::string generate_cb_memory_instance_name(const std::string& prefix, const e_side& cb_side, const size_t& pin_id, const std::string& postfix, - const bool& logical_memory = false); + const bool& feedthrough_memory = false); std::string generate_pb_mux_instance_name(const std::string& prefix, t_pb_graph_pin* pb_graph_pin, diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index ad7856732..55b0ae52f 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -413,12 +413,11 @@ ShellCommandId add_build_fabric_command_template( shell_cmd.set_option_require_value(opt_group_tile, openfpga::OPT_STRING); /* Add an option '--group_config_block' */ - CommandOptionId opt_group_config_block = - shell_cmd.add_option("group_config_block", false, - "group configuration memory blocks under CLB/SB/CB " - "blocks etc. This helps to " - "reduce optimize the density of configuration memory " - "through physical design"); + shell_cmd.add_option("group_config_block", false, + "group configuration memory blocks under CLB/SB/CB " + "blocks etc. This helps to " + "reduce optimize the density of configuration memory " + "through physical design"); /* Add an option '--generate_random_fabric_key' */ shell_cmd.add_option("generate_random_fabric_key", false, diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index 4c3ea3257..0be788f48 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -381,7 +381,7 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, /* Now fpga_core should be the only configurable child under the top-level * module */ - module_manager.add_configurable_child(new_top_module, top_module, 0); + module_manager.add_configurable_child(new_top_module, top_module, 0, false); return status; } diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 6001b00e4..293fa4660 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -966,7 +966,7 @@ static void rec_build_logical_tile_modules( std::vector memory_modules; std::vector memory_instances; - e_config_protocol mem_module_type = group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; + e_config_protocol_type mem_module_type = group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; /* Add all the child Verilog modules as instances */ for (int ichild = 0; ichild < physical_mode->num_pb_type_children; ++ichild) { @@ -1070,7 +1070,7 @@ static void rec_build_logical_tile_modules( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.configurable_children(pb_module).size()) { + if (0 < module_manager.logical_configurable_children(pb_module).size()) { add_module_nets_memory_config_bus(module_manager, decoder_lib, pb_module, mem_module_type, circuit_lib.design_tech_type(sram_model)); @@ -1258,7 +1258,7 @@ static void build_physical_tile_module( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.configurable_children(grid_module).size()) { + if (0 < module_manager.logical_configurable_children(grid_module).size()) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, grid_module, sram_orgz_type, circuit_lib.design_tech_type(sram_model)); diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 51821ffa7..6ee7e2f2e 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1391,7 +1391,7 @@ int add_physical_memory_module(ModuleManager& module_manager, size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH); - std::string phy_mem_module_name = generate_physical_memory_module_name(module_num_config_bits); + std::string phy_mem_module_name = generate_physical_memory_module_name(module_manager.module_name(curr_module), module_num_config_bits); ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); if (!module_manager.valid_module_id(phy_mem_module)) { status = build_memory_group_module(module_manager, decode_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules); @@ -1414,7 +1414,7 @@ int add_physical_memory_module(ModuleManager& module_manager, /* Build nets between the data output of the physical memory module and the outputs of the logical configurable children */ size_t curr_mem_pin_index = 0; - std::map mem2mem_port_map; + std::map mem2mem_port_map; mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 7f29a6279..c3c209f62 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1479,7 +1479,7 @@ static int build_tile_module( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.configurable_children(tile_module).size()) { + if (0 < module_manager.logical_configurable_children(tile_module).size()) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, tile_module, sram_orgz_type, circuit_lib.design_tech_type(sram_model)); diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 92eef9967..5d6ce081c 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -86,6 +86,7 @@ static void organize_top_module_tile_cb_modules( module_manager.add_configurable_child( top_module, cb_module, cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)], + false, config_coord); } } @@ -173,7 +174,7 @@ static void organize_top_module_tile_memory_modules( rr_gsb.get_sb_y() * 2 + 1); module_manager.add_configurable_child( top_module, sb_module, - sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], config_coord); + sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], false, config_coord); } } @@ -218,7 +219,7 @@ static void organize_top_module_tile_memory_modules( sram_model, sram_orgz_type)) { vtr::Point config_coord(tile_coord.x() * 2, tile_coord.y() * 2); module_manager.add_configurable_child( - top_module, grid_module, + top_module, grid_module, false, grid_instance_ids[tile_coord.x()][tile_coord.y()], config_coord); } } @@ -269,14 +270,14 @@ void build_top_module_configurable_regions( "Build configurable regions for the top module"); /* Ensure we have valid configurable children */ - VTR_ASSERT(false == module_manager.configurable_children(top_module).empty()); + VTR_ASSERT(false == module_manager.logical_configurable_children(top_module).empty()); /* Ensure that our region definition is valid */ VTR_ASSERT(1 <= config_protocol.num_regions()); /* Exclude decoders from the list */ size_t num_configurable_children = - module_manager.configurable_children(top_module).size(); + module_manager.logical_configurable_children(top_module).size(); if (CONFIG_MEM_MEMORY_BANK == config_protocol.type() || CONFIG_MEM_QL_MEMORY_BANK == config_protocol.type()) { num_configurable_children -= 2; @@ -291,7 +292,7 @@ void build_top_module_configurable_regions( bool create_region = true; ConfigRegionId curr_region = ConfigRegionId::INVALID(); for (size_t ichild = 0; - ichild < module_manager.configurable_children(top_module).size(); + ichild < module_manager.logical_configurable_children(top_module).size(); ++ichild) { if (true == create_region) { curr_region = module_manager.add_config_region(top_module); @@ -300,8 +301,8 @@ void build_top_module_configurable_regions( /* Add the child to a region */ module_manager.add_configurable_child_to_region( top_module, curr_region, - module_manager.configurable_children(top_module)[ichild], - module_manager.configurable_child_instances(top_module)[ichild], ichild); + module_manager.logical_configurable_children(top_module)[ichild], + module_manager.logical_configurable_child_instances(top_module)[ichild], ichild); /* See if the current region is full or not: * For the last region, we will keep adding until we finish all the children @@ -541,11 +542,11 @@ void shuffle_top_module_configurable_children( /* Cache the configurable children and their instances */ std::vector orig_configurable_children = - module_manager.configurable_children(top_module); + module_manager.logical_configurable_children(top_module); std::vector orig_configurable_child_instances = - module_manager.configurable_child_instances(top_module); + module_manager.logical_configurable_child_instances(top_module); std::vector> orig_configurable_child_coordinates = - module_manager.configurable_child_coordinates(top_module); + module_manager.logical_configurable_child_coordinates(top_module); /* Reorganize the configurable children */ module_manager.clear_configurable_children(top_module); @@ -554,6 +555,7 @@ void shuffle_top_module_configurable_children( module_manager.add_configurable_child( top_module, orig_configurable_children[shuffled_keys[ikey]], orig_configurable_child_instances[shuffled_keys[ikey]], + false, orig_configurable_child_coordinates[shuffled_keys[ikey]]); } @@ -651,7 +653,7 @@ int load_top_module_memory_modules_from_fabric_key( /* Now we can add the child to configurable children of the top module */ module_manager.add_configurable_child(top_module, instance_info.first, - instance_info.second, + instance_info.second, false, fabric_key.key_coordinate(key)); module_manager.add_configurable_child_to_region( top_module, top_module_config_region, instance_info.first, @@ -1330,16 +1332,16 @@ static void add_top_module_nets_cmos_memory_bank_config_bus( * configurable children */ module_manager.add_configurable_child(top_module, bl_decoder_module, - curr_bl_decoder_instance_id); + curr_bl_decoder_instance_id, false); module_manager.add_configurable_child_to_region( top_module, config_region, bl_decoder_module, curr_bl_decoder_instance_id, - module_manager.configurable_children(top_module).size() - 1); + module_manager.logical_configurable_children(top_module).size() - 1); module_manager.add_configurable_child(top_module, wl_decoder_module, - curr_wl_decoder_instance_id); + curr_wl_decoder_instance_id, false); module_manager.add_configurable_child_to_region( top_module, config_region, wl_decoder_module, curr_wl_decoder_instance_id, - module_manager.configurable_children(top_module).size() - 1); + module_manager.logical_configurable_children(top_module).size() - 1); } } @@ -1760,7 +1762,7 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( ++mem_index) { ModuleId child_module = configurable_children[mem_index]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.logical_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 = @@ -1795,7 +1797,7 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( ++mem_index) { ModuleId child_module = configurable_children[mem_index]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.logical_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 = @@ -1815,11 +1817,11 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( /* Add the decoder as the last configurable children */ module_manager.add_configurable_child(parent_module, decoder_module, - decoder_instance); + decoder_instance, false); /* 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); + module_manager.logical_configurable_children(parent_module).size() - 1); } /********************************************************************* diff --git a/openfpga/src/fabric/fabric_key_writer.cpp b/openfpga/src/fabric/fabric_key_writer.cpp index 9a7e41791..b652b5857 100644 --- a/openfpga/src/fabric/fabric_key_writer.cpp +++ b/openfpga/src/fabric/fabric_key_writer.cpp @@ -32,7 +32,7 @@ static int add_module_keys_to_fabric_key(const ModuleManager& module_manager, return CMD_EXEC_SUCCESS; } /* Bypass modules which does not have any configurable children */ - if (module_manager.configurable_children(curr_module).empty()) { + if (module_manager.logical_configurable_children(curr_module).empty()) { return CMD_EXEC_SUCCESS; } /* Now create the module and add subkey one by one */ @@ -41,12 +41,12 @@ static int add_module_keys_to_fabric_key(const ModuleManager& module_manager, return CMD_EXEC_FATAL_ERROR; } size_t num_config_child = - module_manager.configurable_children(curr_module).size(); + module_manager.logical_configurable_children(curr_module).size(); for (size_t ichild = 0; ichild < num_config_child; ++ichild) { ModuleId child_module = - module_manager.configurable_children(curr_module)[ichild]; + module_manager.logical_configurable_children(curr_module)[ichild]; size_t child_instance = - module_manager.configurable_child_instances(curr_module)[ichild]; + module_manager.logical_configurable_child_instances(curr_module)[ichild]; FabricSubKeyId sub_key = fabric_key.create_module_key(key_module_id); fabric_key.set_sub_key_name(sub_key, @@ -111,7 +111,7 @@ int write_fabric_key_to_xml_file( /* Build a fabric key database by visiting all the configurable children */ FabricKey fabric_key; - size_t num_keys = module_manager.configurable_children(top_module).size(); + size_t num_keys = module_manager.logical_configurable_children(top_module).size(); fabric_key.reserve_keys(num_keys); diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 53239eaf0..7371c418a 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -1384,9 +1384,9 @@ void ModuleManager::clear_configurable_children(const ModuleId& parent_module) { logical_configurable_child_regions_[parent_module].clear(); logical_configurable_child_coordinates_[parent_module].clear(); - parent_configurable_children_[parent_module].clear(); - parent_configurable_child_instances_[parent_module].clear(); - parent_configurable_child_parents_[parent_module].clear(); + physical_configurable_children_[parent_module].clear(); + physical_configurable_child_instances_[parent_module].clear(); + physical_configurable_child_parents_[parent_module].clear(); } void ModuleManager::clear_config_region(const ModuleId& parent_module) { diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 0d4a5b2f3..8e459fedc 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -555,8 +555,23 @@ class ModuleManager { physical_configurable_child_instances_; /* Instances of child modules with configurable memory bits that this module contain */ + vtr::vector> + physical_configurable_child_regions_; /* Instances of child modules with configurable + memory bits that this module contain */ + vtr::vector>> + physical_configurable_child_coordinates_; /* Relative coorindates of child modules + with configurable memory bits that this + module contain */ + vtr::vector> - physical_configurable_child_parents_; /* Parent modules with configurable memory bits that + logical2physical_configurable_children_; /* Child modules with configurable memory bits that + this module contain */ + vtr::vector> + logical2physical_configurable_child_instances_; /* Instances of child modules with + configurable memory bits that this module + contain */ + vtr::vector> + logical2physical_configurable_child_parents_; /* Parent modules with configurable memory bits that this module contain */ /* Configurable regions to group the physical configurable children diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 1e283358e..98c3ce4e1 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -35,15 +35,15 @@ static size_t rec_estimate_device_bitstream_num_blocks( * actually configurable memory elements * We skip them in couting */ - if (0 == module_manager.configurable_children(top_module).size()) { + if (0 == module_manager.logical_configurable_children(top_module).size()) { return 0; } size_t num_configurable_children = - module_manager.configurable_children(top_module).size(); + module_manager.logical_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]; + module_manager.logical_configurable_children(top_module)[ichild]; num_blocks += rec_estimate_device_bitstream_num_blocks(module_manager, child_module); } @@ -68,7 +68,7 @@ static size_t rec_estimate_device_bitstream_num_bits( /* 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()) { + if (0 == module_manager.logical_configurable_children(parent_module).size()) { return 1; } @@ -105,7 +105,7 @@ static size_t rec_estimate_device_bitstream_num_bits( VTR_ASSERT_SAFE(parent_module != top_module); size_t num_configurable_children = - module_manager.configurable_children(parent_module).size(); + module_manager.logical_configurable_children(parent_module).size(); /* Frame-based configuration protocol will have 1 decoder * if there are more than 1 configurable children @@ -117,7 +117,7 @@ static size_t rec_estimate_device_bitstream_num_bits( for (size_t ichild = 0; ichild < num_configurable_children; ++ichild) { ModuleId child_module = - module_manager.configurable_children(parent_module)[ichild]; + module_manager.logical_configurable_children(parent_module)[ichild]; num_bits += rec_estimate_device_bitstream_num_bits( module_manager, top_module, child_module, config_protocol); } diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index a5341217f..ddd51c053 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -74,12 +74,12 @@ static void rec_build_module_fabric_dependent_chain_bitstream( } else { for (size_t child_id = 0; child_id < - module_manager.configurable_children(parent_module).size(); + module_manager.logical_configurable_children(parent_module).size(); ++child_id) { ModuleId child_module = - module_manager.configurable_children(parent_module)[child_id]; + module_manager.logical_configurable_children(parent_module)[child_id]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[child_id]; + module_manager.logical_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); @@ -196,7 +196,7 @@ static void rec_build_module_fabric_dependent_memory_bank_bitstream( * - no need to exclude decoders as they are not there */ std::vector configurable_children = - module_manager.configurable_children(parent_module); + module_manager.logical_configurable_children(parent_module); size_t num_configurable_children = configurable_children.size(); @@ -212,7 +212,7 @@ static void rec_build_module_fabric_dependent_memory_bank_bitstream( ++child_id) { ModuleId child_module = configurable_children[child_id]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[child_id]; + module_manager.logical_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( @@ -324,9 +324,9 @@ static void rec_build_module_fabric_dependent_frame_bitstream( } else { VTR_ASSERT(top_module != parent_module); configurable_children = - module_manager.configurable_children(parent_module); + module_manager.logical_configurable_children(parent_module); configurable_child_instances = - module_manager.configurable_child_instances(parent_module); + module_manager.logical_configurable_child_instances(parent_module); } size_t num_configurable_children = configurable_children.size(); @@ -361,9 +361,9 @@ static void rec_build_module_fabric_dependent_frame_bitstream( * configurable children in all the regions */ for (const ModuleId& child_module : - module_manager.configurable_children(parent_module)) { + module_manager.logical_configurable_children(parent_module)) { /* Bypass any decoder module (which no configurable children */ - if (module_manager.configurable_children(child_module).empty()) { + if (module_manager.logical_configurable_children(child_module).empty()) { continue; } const ModulePortId& child_addr_port_id = @@ -494,7 +494,7 @@ static void rec_build_module_fabric_dependent_frame_bitstream( } else { VTR_ASSERT(top_module != parent_modules.back()); configurable_children = - module_manager.configurable_children(parent_modules.back()); + module_manager.logical_configurable_children(parent_modules.back()); } ModuleId decoder_module = configurable_children.back(); diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp index 97fe97d90..183ed5975 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp @@ -123,7 +123,7 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( * - no need to exclude decoders as they are not there */ std::vector configurable_children = - module_manager.configurable_children(parent_module); + module_manager.logical_configurable_children(parent_module); size_t num_configurable_children = configurable_children.size(); @@ -139,7 +139,7 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( ++child_id) { ModuleId child_module = configurable_children[child_id]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[child_id]; + module_manager.logical_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( diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 19aa076c6..3f642b5fc 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -636,7 +636,7 @@ static void rec_build_physical_block_bitstream( VTR_ASSERT(true == module_manager.valid_module_id(pb_module)); /* Skip module with no configurable children */ - if (0 == module_manager.configurable_children(pb_module).size()) { + if (0 == module_manager.logical_configurable_children(pb_module).size()) { return; } diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index ca384d99d..ee1e48045 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -89,8 +89,8 @@ size_t count_module_manager_module_configurable_children( const ModuleManager& module_manager, const ModuleId& module) { size_t num_config_children = 0; - for (const ModuleId& child : module_manager.configurable_children(module)) { - if (0 != module_manager.configurable_children(child).size()) { + for (const ModuleId& child : module_manager.logical_configurable_children(module)) { + if (0 != module_manager.logical_configurable_children(child).size()) { num_config_children++; } } From 27cae41123fbc8bfc62a6a6166d46169d690a8ca Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 2 Aug 2023 20:37:27 -0700 Subject: [PATCH 08/41] [core] rework physical and logical types of configurable child --- openfpga/src/fabric/module_manager.cpp | 188 ++++++++++++++----------- openfpga/src/fabric/module_manager.h | 75 +++++----- 2 files changed, 149 insertions(+), 114 deletions(-) diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 7371c418a..201b3e2d1 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -77,56 +77,65 @@ std::vector ModuleManager::child_module_instances( } /* Find all the configurable child modules under a parent module */ -std::vector ModuleManager::logical_configurable_children( - const ModuleId& parent_module) const { - /* Validate the module_id */ - VTR_ASSERT(valid_module_id(parent_module)); - - return logical_configurable_children_[parent_module]; -} - -/* Find all the instances of configurable child modules under a parent module */ -std::vector ModuleManager::logical_configurable_child_instances( - const ModuleId& parent_module) const { - /* Validate the module_id */ - VTR_ASSERT(valid_module_id(parent_module)); - - return logical_configurable_child_instances_[parent_module]; -} - -std::vector> ModuleManager::logical_configurable_child_coordinates( - const ModuleId& parent_module) const { - /* Validate the module_id */ - VTR_ASSERT(valid_module_id(parent_module)); - - return logical_configurable_child_coordinates_[parent_module]; -} - -/* Find all the configurable child modules under a parent module */ -std::vector ModuleManager::physical_configurable_children( - const ModuleId& parent_module) const { +std::vector ModuleManager::configurable_children( + const ModuleId& parent_module, const e_config_child_type& type) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); + if (type == ModuleManager::e_config_child_type::LOGICAL) { + return logical_configurable_children_[parent_module]; + } + VTR_ASSERT(type == ModuleManager::e_config_child_type::PHYSICAL); return physical_configurable_children_[parent_module]; } /* Find all the instances of configurable child modules under a parent module */ -std::vector ModuleManager::physical_configurable_child_instances( - const ModuleId& parent_module) const { +std::vector ModuleManager::configurable_child_instances( + const ModuleId& parent_module, const e_config_child_type& type) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); + if (type == ModuleManager::e_config_child_type::LOGICAL) { + return logical_configurable_child_instances_[parent_module]; + } + VTR_ASSERT(type == ModuleManager::e_config_child_type::PHYSICAL); return physical_configurable_child_instances_[parent_module]; } -/* Find all the instances of configurable child modules under a parent module */ -std::vector ModuleManager::physical_configurable_child_parents( +std::vector> ModuleManager::configurable_child_coordinates( + const ModuleId& parent_module, const e_config_child_type& type) const { + /* Validate the module_id */ + VTR_ASSERT(valid_module_id(parent_module)); + VTR_ASSERT(type == ModuleManager::e_config_child_type::PHYSICAL); + + return physical_configurable_child_coordinates_[parent_module]; +} + +/* Find all the configurable child modules under a parent module */ +std::vector ModuleManager::logical2physical_configurable_children( const ModuleId& parent_module) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); - return physical_configurable_child_parents_[parent_module]; + return logical2physical_configurable_children_[parent_module]; +} + +/* Find all the instances of configurable child modules under a parent module */ +std::vector ModuleManager::logical2physical_configurable_child_instances( + const ModuleId& parent_module) const { + /* Validate the module_id */ + VTR_ASSERT(valid_module_id(parent_module)); + + return logical2physical_configurable_child_instances_[parent_module]; +} + +/* Find all the instances of configurable child modules under a parent module */ +std::vector ModuleManager::logical2physical_configurable_child_parents( + const ModuleId& parent_module) const { + /* Validate the module_id */ + VTR_ASSERT(valid_module_id(parent_module)); + + return logical2physical_configurable_child_parents_[parent_module]; } /* Find all the configurable child modules under a parent module */ @@ -193,7 +202,7 @@ std::vector ModuleManager::region_configurable_children( for (const size_t& child_id : config_region_children_[parent_module][region]) { region_config_children.push_back( - logical_configurable_children_[parent_module][child_id]); + physical_configurable_children_[parent_module][child_id]); } return region_config_children; @@ -212,7 +221,7 @@ std::vector ModuleManager::region_configurable_child_instances( for (const size_t& child_id : config_region_children_[parent_module][region]) { region_config_child_instances.push_back( - logical_configurable_child_instances_[parent_module][child_id]); + physical_configurable_child_instances_[parent_module][child_id]); } return region_config_child_instances; @@ -939,7 +948,7 @@ void ModuleManager::set_child_instance_name(const ModuleId& parent_module, void ModuleManager::add_configurable_child(const ModuleId& parent_module, const ModuleId& child_module, const size_t& child_instance, - const bool& logical_only, + const e_config_child_type& type, const vtr::Point coord) { /* Validate the id of both parent and child modules */ VTR_ASSERT(valid_module_id(parent_module)); @@ -947,72 +956,89 @@ void ModuleManager::add_configurable_child(const ModuleId& parent_module, /* Ensure that the instance id is in range */ VTR_ASSERT(child_instance < num_instance(parent_module, child_module)); - logical_configurable_children_[parent_module].push_back(child_module); - logical_configurable_child_instances_[parent_module].push_back(child_instance); - logical_configurable_child_regions_[parent_module].push_back( - ConfigRegionId::INVALID()); - logical_configurable_child_coordinates_[parent_module].push_back(coord); - - if (!logical_only) { + if (type == ModuleManager::e_config_child_type::LOGICAL || type == ModuleManager::e_config_child_type::UNIFIED) { + logical_configurable_children_[parent_module].push_back(child_module); + logical_configurable_child_instances_[parent_module].push_back(child_instance); + } + if (type == ModuleManager::e_config_child_type::PHYSICAL || type == ModuleManager::e_config_child_type::UNIFIED) { physical_configurable_children_[parent_module].push_back(child_module); physical_configurable_child_instances_[parent_module].push_back(child_instance); - physical_configurable_child_parents_[parent_module].push_back(parent_module); - } else { - physical_configurable_children_[parent_module].emplace_back(); - physical_configurable_child_instances_[parent_module].emplace_back(); - physical_configurable_child_parents_[parent_module].emplace_back(); + physical_configurable_child_regions_[parent_module].push_back( + ConfigRegionId::INVALID()); + physical_configurable_child_coordinates_[parent_module].push_back(coord); + } + + if (type == ModuleManager::e_config_child_type::UNIFIED) { + logical2physical_configurable_children_[parent_module].push_back(child_module); + logical2physical_configurable_child_instances_[parent_module].push_back(child_instance); + logical2physical_configurable_child_parents_[parent_module].push_back(parent_module); + } else if (type == ModuleManager::e_config_child_type::LOGICAL) { + logical2physical_configurable_children_[parent_module].emplace_back(); + logical2physical_configurable_child_instances_[parent_module].emplace_back(); + logical2physical_configurable_child_parents_[parent_module].emplace_back(); } } -void ModuleManager::set_physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module) { +void ModuleManager::set_logical2physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); /* Create the pair */ - physical_configurable_children_[parent_module][logical_child_id] = physical_child_module; + logical2physical_configurable_children_[parent_module][logical_child_id] = physical_child_module; } -void ModuleManager::set_physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance) { +void ModuleManager::set_logical2physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); /* Create the pair */ - physical_configurable_child_instances_[parent_module][logical_child_id] = physical_child_instance; + logical2physical_configurable_child_instances_[parent_module][logical_child_id] = physical_child_instance; } -void ModuleManager::set_physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module) { +void ModuleManager::set_logical2physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); /* Create the pair */ - physical_configurable_child_parents_[parent_module][logical_child_id] = physical_child_parent_module; + logical2physical_configurable_child_parents_[parent_module][logical_child_id] = physical_child_parent_module; } void ModuleManager::reserve_configurable_child(const ModuleId& parent_module, - const size_t& num_children) { + const size_t& num_children, + const e_config_child_type& type) { VTR_ASSERT(valid_module_id(parent_module)); - /* Do reserve when the number of children is larger than current size of lists - */ - if (num_children > logical_configurable_children_[parent_module].size()) { - logical_configurable_children_[parent_module].reserve(num_children); + if (type == ModuleManager::e_config_child_type::LOGICAL || type == ModuleManager::e_config_child_type::UNIFIED) { + /* Do reserve when the number of children is larger than current size of lists + */ + if (num_children > logical_configurable_children_[parent_module].size()) { + logical_configurable_children_[parent_module].reserve(num_children); + } + if (num_children > logical_configurable_child_instances_[parent_module].size()) { + logical_configurable_child_instances_[parent_module].reserve(num_children); + } + if (num_children > logical2physical_configurable_children_[parent_module].size()) { + logical2physical_configurable_children_[parent_module].reserve(num_children); + } + if (num_children > logical2physical_configurable_child_instances_[parent_module].size()) { + logical2physical_configurable_child_instances_[parent_module].reserve(num_children); + } + if (num_children > logical2physical_configurable_child_parents_[parent_module].size()) { + logical2physical_configurable_child_parents_[parent_module].reserve(num_children); + } } - if (num_children > logical_configurable_child_instances_[parent_module].size()) { - logical_configurable_child_instances_[parent_module].reserve(num_children); - } - if (num_children > logical_configurable_child_regions_[parent_module].size()) { - logical_configurable_child_regions_[parent_module].reserve(num_children); - } - if (num_children > logical_configurable_child_coordinates_[parent_module].size()) { - logical_configurable_child_coordinates_[parent_module].reserve(num_children); - } - if (num_children > physical_configurable_children_[parent_module].size()) { - physical_configurable_children_[parent_module].reserve(num_children); - } - if (num_children > physical_configurable_child_instances_[parent_module].size()) { - physical_configurable_child_instances_[parent_module].reserve(num_children); - } - if (num_children > physical_configurable_child_parents_[parent_module].size()) { - physical_configurable_child_parents_[parent_module].reserve(num_children); + if (type == ModuleManager::e_config_child_type::PHYSICAL || type == ModuleManager::e_config_child_type::UNIFIED) { + if (num_children > physical_configurable_children_[parent_module].size()) { + physical_configurable_children_[parent_module].reserve(num_children); + } + if (num_children > physical_configurable_child_instances_[parent_module].size()) { + physical_configurable_child_instances_[parent_module].reserve(num_children); + } + if (num_children > physical_configurable_child_regions_[parent_module].size()) { + physical_configurable_child_regions_[parent_module].reserve(num_children); + } + if (num_children > physical_configurable_child_coordinates_[parent_module].size()) { + physical_configurable_child_coordinates_[parent_module].reserve(num_children); + } } } @@ -1041,23 +1067,23 @@ void ModuleManager::add_configurable_child_to_region( /* Ensure that the child module is in the configurable children list */ VTR_ASSERT(child_module == - logical_configurable_children(parent_module)[config_child_id]); + physical_configurable_children(parent_module)[config_child_id]); VTR_ASSERT(child_instance == - logical_configurable_child_instances(parent_module)[config_child_id]); + physical_configurable_child_instances(parent_module)[config_child_id]); /* If the child is already in another region, error out */ if ((true == valid_region_id( parent_module, - logical_configurable_child_regions_[parent_module][config_child_id])) && + physical_configurable_child_regions_[parent_module][config_child_id])) && (config_region != - logical_configurable_child_regions_[parent_module][config_child_id])) { + physical_configurable_child_regions_[parent_module][config_child_id])) { VTR_LOGF_ERROR( __FILE__, __LINE__, "Try to add a configurable child '%s[%lu]' to region '%lu' which is " "already added to another region '%lu'!\n", module_name(child_module).c_str(), child_instance, size_t(config_region), - size_t(logical_configurable_child_regions_[parent_module][config_child_id])); + size_t(physical_configurable_child_regions_[parent_module][config_child_id])); exit(1); } diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 8e459fedc..8f3b0f9aa 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -68,6 +68,18 @@ class ModuleManager { NUM_MODULE_USAGE_TYPES }; + /* Type of configurable child: + * - logical: represent a logical configurable block, which may not contain a physical memory inside + * - physical: represent a physical configurable block, which contains a physical memory inside + * - unified: a unified block whose physical memory is also the logical memory + */ + enum class e_config_child_type { + LOGICAL, + PHYSICAL, + UNIFIED, + NUM_TYPES + }; + public: /* Public Constructors */ public: /* Type implementations */ /* @@ -166,28 +178,28 @@ class ModuleManager { std::vector child_module_instances( const ModuleId& parent_module, const ModuleId& child_module) const; /* Find all the configurable child modules under a parent module */ - std::vector logical_configurable_children( - const ModuleId& parent_module) const; + std::vector configurable_children( + const ModuleId& parent_module, const e_config_child_type& type) const; /* Find all the instances of configurable child modules under a parent module */ - std::vector logical_configurable_child_instances( - const ModuleId& parent_module) const; + std::vector configurable_child_instances( + const ModuleId& parent_module, const e_config_child_type& type) const; /* Find the coordindate of a configurable child module under a parent module */ - std::vector> logical_configurable_child_coordinates( - const ModuleId& parent_module) const; + std::vector> configurable_child_coordinates( + const ModuleId& parent_module, const e_config_child_type& type) const; /* Find all the configurable child modules under a parent module */ - std::vector physical_configurable_children( + std::vector logical2physical_configurable_children( const ModuleId& parent_module) const; /* Find all the instances of configurable child modules under a parent module */ - std::vector physical_configurable_child_instances( + std::vector logical2physical_configurable_child_instances( const ModuleId& parent_module) const; /* Find all the parent modules of physical configurable child modules under a parent module * Note that a physical configurable child module may be at another module; Only the logical child module is under the current parent module */ - std::vector physical_configurable_child_parents( + std::vector logical2physical_configurable_child_parents( const ModuleId& parent_module) const; /* Find all the I/O child modules under a parent module */ @@ -374,22 +386,23 @@ class ModuleManager { void add_configurable_child( const ModuleId& module, const ModuleId& child_module, const size_t& child_instance, - const bool& logical_only, + const e_config_child_type& type, const vtr::Point coord = vtr::Point(-1, -1)); /** @brief Create a pair of mapping from a logical configurable child to a physical configurable child */ - void set_physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module); + void set_logical2physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module); /** @brief Create a pair of mapping from a logical configurable child to a physical configurable child */ - void set_physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance); - void set_physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module); + void set_logical2physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance); + void set_logical2physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module); /* Reserved a number of configurable children for memory efficiency */ void reserve_configurable_child(const ModuleId& module, - const size_t& num_children); + const size_t& num_children, + const e_config_child_type& type); /* Create a new configurable region under a module */ ConfigRegionId add_config_region(const ModuleId& module); /* Add a configurable child module to a region * Note: - * - The child module must be added as a configurable child to the parent + * - The child module must be added as a physical configurable child to the parent * module before calling this function! */ void add_configurable_child_to_region(const ModuleId& parent_module, @@ -533,6 +546,9 @@ class ModuleManager { * is configured first, etc. Note that the sequence can be totally different * from the children_ list This is really dependent how the configuration * protocol is organized which should be made by users/designers + * Note that there could be two types of configurable children under a module + * - logical: only contains virtual/feedthough memory blocks. A logical configurable child can only contain logical subchild. Logical memory block is required for architecture bitstream generation, because it carries logical information (the location of memory to its programmable resources) + * - physical: contains physical memory blocks. Logical memory blocks are mapped to the physical memory block. A physical memory block may contain coordinates and configuration regions which are required for fabric bitstream generation. */ vtr::vector> logical_configurable_children_; /* Child modules with configurable memory bits that @@ -541,13 +557,17 @@ class ModuleManager { logical_configurable_child_instances_; /* Instances of child modules with configurable memory bits that this module contain */ - vtr::vector> - logical_configurable_child_regions_; /* Instances of child modules with configurable - memory bits that this module contain */ - vtr::vector>> - logical_configurable_child_coordinates_; /* Relative coorindates of child modules - with configurable memory bits that this - module contain */ + vtr::vector> + logical2physical_configurable_children_; /* Child modules with configurable memory bits that + this module contain */ + vtr::vector> + logical2physical_configurable_child_instances_; /* Instances of child modules with + configurable memory bits that this module + contain */ + vtr::vector> + logical2physical_configurable_child_parents_; /* Parent modules with configurable memory bits that + this module contain */ + vtr::vector> physical_configurable_children_; /* Child modules with configurable memory bits that this module contain */ @@ -563,17 +583,6 @@ class ModuleManager { with configurable memory bits that this module contain */ - vtr::vector> - logical2physical_configurable_children_; /* Child modules with configurable memory bits that - this module contain */ - vtr::vector> - logical2physical_configurable_child_instances_; /* Instances of child modules with - configurable memory bits that this module - contain */ - vtr::vector> - logical2physical_configurable_child_parents_; /* Parent modules with configurable memory bits that - this module contain */ - /* Configurable regions to group the physical configurable children * Note: * - Each child can only be added a group From 5895a1d96bce3197b08e5bf956dbee2e08c5b42c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 2 Aug 2023 22:50:19 -0700 Subject: [PATCH 09/41] [core] reworking fabric generator based on latest changes on configurable children --- openfpga/src/fabric/build_grid_modules.cpp | 45 ++++--- openfpga/src/fabric/build_memory_modules.cpp | 2 +- openfpga/src/fabric/module_manager.cpp | 8 +- openfpga/src/fabric/module_manager.h | 2 +- openfpga/src/utils/memory_utils.cpp | 24 ++-- openfpga/src/utils/module_manager_utils.cpp | 117 +++++++++++-------- openfpga/src/utils/module_manager_utils.h | 18 ++- 7 files changed, 130 insertions(+), 86 deletions(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 293fa4660..09b3566fa 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -357,16 +357,17 @@ static void build_primitive_block_module( module_manager, primitive_module, logic_module, logic_instance_id, memory_module, memory_instance_id, circuit_lib, primitive_model); /* Record memory-related information */ - size_t config_child_id = module_manager.num_configurable_children(primitive_module); + size_t config_child_id = module_manager.num_configurable_children(primitive_module, ModuleManager::e_config_child_type::LOGICAL); module_manager.add_configurable_child(primitive_module, memory_module, - memory_instance_id, group_config_block); + memory_instance_id, + group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); /* For logical memory, define the physical memory here */ if (group_config_block) { std::string physical_memory_module_name = generate_memory_module_name(circuit_lib, primitive_model, sram_model, std::string(MEMORY_MODULE_POSTFIX), false); ModuleId physical_memory_module = module_manager.find_module(physical_memory_module_name); - module_manager.set_physical_configurable_child(primitive_module, config_child_id, physical_memory_module); + module_manager.set_logical2physical_configurable_child(primitive_module, config_child_id, physical_memory_module); } } @@ -374,10 +375,11 @@ static void build_primitive_block_module( * primitive modules This is a one-shot addition that covers all the memory * modules in this primitive module! */ - if (0 < module_manager.configurable_children(primitive_module).size()) { + if (0 < module_manager.num_configurable_children(primitive_module, ModuleManager::e_config_child_type::LOGICAL)) { add_module_nets_memory_config_bus(module_manager, decoder_lib, primitive_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model)); + circuit_lib.design_tech_type(sram_model), + ModuleManager::e_config_child_type::LOGICAL); } /* Add global ports to the pb_module: @@ -639,6 +641,11 @@ static void add_module_pb_graph_pin_interc( std::string mux_mem_module_name = generate_mux_subckt_name(circuit_lib, interc_circuit_model, fan_in, std::string(MEMORY_MODULE_POSTFIX)); + if (group_config_block) { + mux_mem_module_name = + generate_mux_subckt_name(circuit_lib, interc_circuit_model, fan_in, + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + } ModuleId mux_mem_module = module_manager.find_module(mux_mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(mux_mem_module)); size_t mux_mem_instance = @@ -653,8 +660,18 @@ static void add_module_pb_graph_pin_interc( module_manager.set_child_instance_name( pb_module, mux_mem_module, mux_mem_instance, mux_mem_instance_name); /* Add this MUX as a configurable child to the pb_module */ + size_t config_child_id = module_manager.num_configurable_child(pb_module, ModuleManager::e_config_child_type::LOGICAL); module_manager.add_configurable_child(pb_module, mux_mem_module, - mux_mem_instance, group_config_block); + mux_mem_instance, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); + if (group_config_block) { + std::string phy_mem_module_name = + generate_mux_subckt_name(circuit_lib, interc_circuit_model, fan_in, + std::string(MEMORY_MODULE_POSTFIX)); + ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(phy_mem_module)); + module_manager.set_logical2physical_configurable_child(pb_module, config_child_id, + phy_mem_module); + } /* Add nets to connect SRAM ports of the MUX to the SRAM port of memory * module */ @@ -1012,7 +1029,7 @@ static void rec_build_logical_tile_modules( circuit_lib, sram_model, mem_module_type)) { module_manager.add_configurable_child(pb_module, child_pb_module, - child_instance_id, group_config_block); + child_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); } } } @@ -1070,10 +1087,11 @@ static void rec_build_logical_tile_modules( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.logical_configurable_children(pb_module).size()) { + if (0 < module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::LOGICAL)) { add_module_nets_memory_config_bus(module_manager, decoder_lib, pb_module, mem_module_type, - circuit_lib.design_tech_type(sram_model)); + circuit_lib.design_tech_type(sram_model), + ModuleManager::e_config_child_type::LOGICAL); } VTR_LOGV(verbose, "Done\n"); @@ -1150,7 +1168,7 @@ static void build_physical_tile_module( group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type)) { /* Only add logical configurable children here. Since we will add a physical memory block at this level */ module_manager.add_configurable_child(grid_module, pb_module, - pb_instance_id, group_config_block); + pb_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); } } } @@ -1245,9 +1263,10 @@ static void build_physical_tile_module( * we just need to find all the I/O ports from the child modules and build a * list of it */ + ModuleManager::e_config_child_type config_child_type = group_config_block ? ModuleManager::e_config_child_type::PHYSICAL : ModuleManager::e_config_child_type::LOGICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, grid_module, circuit_lib, sram_model, sram_orgz_type); + module_manager, grid_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, grid_module, circuit_lib, sram_model, sram_orgz_type, @@ -1258,10 +1277,10 @@ static void build_physical_tile_module( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.logical_configurable_children(grid_module).size()) { + if (0 < module_manager.num_configurable_children(grid_module, config_child_type)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, grid_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model)); + circuit_lib.design_tech_type(sram_model), config_child_type); } VTR_LOGV(verbose, "Done\n"); diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 6ee7e2f2e..4b5a9616b 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1410,7 +1410,7 @@ int add_physical_memory_module(ModuleManager& module_manager, module_manager.add_child_module(curr_module, phy_mem_module, false); /* Register in the physical configurable children list */ - module_manager.add_physical_configurable_child(curr_module, phy_mem_module, phy_mem_instance, curr_module); + module_manager.add_configurable_child(curr_module, phy_mem_module, phy_mem_instance, ModuleManager::e_config_child_type::PHYSICAL); /* Build nets between the data output of the physical memory module and the outputs of the logical configurable children */ size_t curr_mem_pin_index = 0; diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 201b3e2d1..ca36a9b9f 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -412,9 +412,13 @@ size_t ModuleManager::instance_id(const ModuleId& parent_module, return size_t(-1); } -size_t ModuleManager::num_logical_configurable_children(const ModuleId& parent_module) const { +size_t ModuleManager::num_configurable_children(const ModuleId& parent_module, const e_config_child_type& type) const; VTR_ASSERT(valid_module_id(parent_module)); - return logical_configurable_children_[parent_module].size(); + if (type == ModuleManager::e_config_child_type::LOGICAL) { + return logical_configurable_children_[parent_module].size(); + } + VTR_ASSERT(type == ModuleManager::e_config_child_type::LOGICAL); + return physical_configurable_children_[parent_module].size() } ModuleManager::e_module_port_type ModuleManager::port_type( diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 8f3b0f9aa..f57f62361 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -265,7 +265,7 @@ class ModuleManager { const ModuleId& child_module, const std::string& instance_name) const; /** @brief Count the number of logical configurable children */ - size_t num_logical_configurable_children(const ModuleId& parent_module) const; + size_t num_configurable_children(const ModuleId& parent_module, const e_config_child_type& type) const; /* Find the type of a port */ ModuleManager::e_module_port_type port_type(const ModuleId& module, const ModulePortId& port) const; diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index 7d3b6b23f..e6c6a0952 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -497,14 +497,14 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( } int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector& physical_memory_children) { - if (module_manager.logical_configurable_children(curr_module).empty()) { + if (module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).empty()) { return CMD_EXEC_SUCCESS; } - for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { - ModuleId logical_child = module_manager.logical_configurable_children(curr_module)[ichild]; - if (module_manager.logical_configurable_children(logical_child).empty()) { + for (size_t ichild = 0; ichild < module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).size(); ++ichild) { + ModuleId logical_child = module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; + if (module_manager.configurable_children(logical_child, ModuleManager::e_config_child_type::LOGICAL).empty()) { /* This is a leaf node, get the physical memory module */ - physical_memory_children.push_back(module_manager.physical_configurable_children(curr_module)[ichild]); + physical_memory_children.push_back(module_manager.logical2physical_configurable_children(curr_module)[ichild]); } else { rec_find_physical_memory_children(module_manager, logical_child, physical_memory_children); } @@ -513,20 +513,20 @@ int rec_find_physical_memory_children(const ModuleManager& module_manager, const } int rec_update_logical_memory_children_with_physical_mapping(ModuleManager& module_manager, const ModuleId& curr_module, const ModuleId& phy_mem_module, std::map& logical_mem_child_inst_count) { - if (module_manager.logical_configurable_children(curr_module).empty()) { + if (module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).empty()) { return CMD_EXEC_SUCCESS; } - for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { - ModuleId logical_child = module_manager.logical_configurable_children(curr_module)[ichild]; - if (module_manager.logical_configurable_children(logical_child).empty()) { + for (size_t ichild = 0; ichild < module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).size(); ++ichild) { + ModuleId logical_child = module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; + if (module_manager.configurable_children(logical_child, ModuleManager::e_config_child_type::LOGICAL).empty()) { /* This is a leaf node, update its physical information */ - ModuleId phy_mem_submodule = module_manager.physical_configurable_children(curr_module)[ichild] + ModuleId phy_mem_submodule = module_manager.logical2physical_configurable_children(curr_module)[ichild] auto result = logical_mem_child_inst_count.find(phy_mem_submodule); if (result == logical_mem_child_inst_count.end()) { logical_mem_child_inst_count.find[phy_mem_submodule] = 0; } - module_manager.set_physical_configurable_child_instance(curr_module, ichild, logical_mem_child_inst_count[phy_mem_submodule]); - module_manager.set_physical_configurable_child_parent_module(curr_module, ichild, phy_mem_module); + module_manager.set_logical2physical_configurable_child_instance(curr_module, ichild, logical_mem_child_inst_count[phy_mem_submodule]); + module_manager.set_logical2physical_configurable_child_parent_module(curr_module, ichild, phy_mem_module); logical_mem_child_inst_count[phy_mem_submodule]++; } else { rec_find_physical_memory_children(module_manager, logical_child, physical_memory_children, logical_mem_child_inst_count); diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index ee1e48045..63252791f 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -1018,7 +1018,8 @@ void add_module_nets_between_logic_and_memory_sram_bus( void add_module_nets_cmos_flatten_memory_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_port_type& config_port_type) { + const e_circuit_model_port_type& config_port_type, + const ModuleManager::e_config_child_type& config_child_type) { /* A counter for the current pin id for the source port of parent module */ size_t cur_src_pin_id = 0; @@ -1040,7 +1041,7 @@ void add_module_nets_cmos_flatten_memory_config_bus( module_manager.module_port(net_src_module_id, net_src_port_id); for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.num_configurable_children(parent_module, config_child_type); ++mem_index) { ModuleId net_sink_module_id; size_t net_sink_instance_id; @@ -1050,9 +1051,9 @@ void add_module_nets_cmos_flatten_memory_config_bus( std::string sink_port_name = generate_sram_port_name(sram_orgz_type, config_port_type); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); @@ -1273,9 +1274,10 @@ void add_module_nets_cmos_memory_bank_wl_config_bus( *********************************************************************/ void add_module_nets_cmos_memory_chain_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.num_configurable_children(parent_module, config_child_type); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -1297,27 +1299,27 @@ void add_module_nets_cmos_memory_chain_config_bus( /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); net_src_module_id = - module_manager.configurable_children(parent_module)[mem_index - 1]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[mem_index - 1]; + parent_module, config_child_type)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -1351,9 +1353,9 @@ void add_module_nets_cmos_memory_chain_config_bus( /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module).back(); + module_manager.configurable_children(parent_module, config_child_type).back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module).back(); + module_manager.configurable_child_instances(parent_module, config_child_type).back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -1407,9 +1409,10 @@ void add_module_nets_cmos_memory_chain_config_bus( * *********************************************************************/ static void add_module_nets_cmos_memory_frame_short_config_bus( - ModuleManager& module_manager, const ModuleId& parent_module) { + ModuleManager& module_manager, const ModuleId& parent_module, + const ModuleManager::e_config_child_type& config_child_type) { std::vector configurable_children = - module_manager.configurable_children(parent_module); + module_manager.configurable_children(parent_module, config_child_type); VTR_ASSERT(1 == configurable_children.size()); ModuleId child_module = configurable_children[0]; @@ -1491,9 +1494,10 @@ static void add_module_nets_cmos_memory_frame_short_config_bus( *********************************************************************/ static void add_module_nets_cmos_memory_frame_decoder_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, - const ModuleId& parent_module) { + const ModuleId& parent_module, + const ModuleManager::e_config_child_type& config_child_type) { std::vector configurable_children = - module_manager.configurable_children(parent_module); + module_manager.configurable_children(parent_module, config_child_type); /* Find the decoder specification */ size_t addr_size = @@ -1572,7 +1576,7 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( ++mem_index) { ModuleId child_module = configurable_children[mem_index]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; ModulePortId child_addr_port = module_manager.find_module_port( child_module, std::string(DECODER_ADDRESS_PORT_NAME)); BasicPort child_addr_port_info = @@ -1604,7 +1608,7 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( ++mem_index) { ModuleId child_module = configurable_children[mem_index]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; ModulePortId child_din_port = module_manager.find_module_port( child_module, std::string(DECODER_DATA_IN_PORT_NAME)); add_module_bus_nets(module_manager, parent_module, parent_module, 0, @@ -1625,7 +1629,7 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( ++mem_index) { ModuleId child_module = configurable_children[mem_index]; size_t child_instance = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; ModulePortId child_en_port = module_manager.find_module_port( child_module, std::string(DECODER_ENABLE_PORT_NAME)); BasicPort child_en_port_info = @@ -1649,7 +1653,7 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( } /* Add the decoder as the last configurable children */ - module_manager.add_configurable_child(parent_module, decoder_module, 0); + module_manager.add_configurable_child(parent_module, decoder_module, 0, config_child_type); } /********************************************************************* @@ -1663,18 +1667,19 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( **********************************************************************/ void add_module_nets_cmos_memory_frame_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, - const ModuleId& parent_module) { - if (0 == module_manager.configurable_children(parent_module).size()) { + const ModuleId& parent_module, + const ModuleManager::e_config_child_type& config_child_type) { + if (0 == module_manager.num_configurable_children(parent_module, config_child_type)) { return; } - if (1 == module_manager.configurable_children(parent_module).size()) { + if (1 == module_manager.num_configurable_children(parent_module, config_child_type)) { add_module_nets_cmos_memory_frame_short_config_bus(module_manager, - parent_module); + parent_module, config_child_type); } else { - VTR_ASSERT(1 < module_manager.configurable_children(parent_module).size()); + VTR_ASSERT(1 < module_manager.num_configurable_children(parent_module, config_child_type)); add_module_nets_cmos_memory_frame_decoder_config_bus( - module_manager, decoder_lib, parent_module); + module_manager, decoder_lib, parent_module, config_child_type); } } @@ -1724,27 +1729,33 @@ void add_module_nets_cmos_memory_frame_config_bus( **********************************************************************/ static void add_module_nets_cmos_memory_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, - const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type) { + const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { switch (sram_orgz_type) { case CONFIG_MEM_SCAN_CHAIN: { add_module_nets_cmos_memory_chain_config_bus( - module_manager, parent_module, sram_orgz_type); + module_manager, parent_module, sram_orgz_type, config_child_type); break; } case CONFIG_MEM_FEEDTHROUGH: + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BLB, config_child_type); + break; case CONFIG_MEM_STANDALONE: case CONFIG_MEM_QL_MEMORY_BANK: case CONFIG_MEM_MEMORY_BANK: add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, config_child_type); add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR, config_child_type); break; case CONFIG_MEM_FRAME_BASED: add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, - parent_module); + parent_module, config_child_type); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, @@ -1790,31 +1801,32 @@ static void add_module_nets_cmos_memory_config_bus( **********************************************************************/ static void add_pb_module_nets_cmos_memory_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, - const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type) { + const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { switch (sram_orgz_type) { case CONFIG_MEM_SCAN_CHAIN: { add_module_nets_cmos_memory_chain_config_bus( - module_manager, parent_module, sram_orgz_type); + module_manager, parent_module, sram_orgz_type, config_child_type); break; } case CONFIG_MEM_STANDALONE: case CONFIG_MEM_QL_MEMORY_BANK: add_module_nets_cmos_memory_bank_bl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, config_child_type); add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR, config_child_type); break; case CONFIG_MEM_MEMORY_BANK: add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, config_child_type); break; case CONFIG_MEM_FRAME_BASED: add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, - parent_module); + parent_module, config_child_type); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, @@ -1880,11 +1892,12 @@ static void add_pb_module_nets_cmos_memory_config_bus( void add_module_nets_memory_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_design_tech& mem_tech) { + const e_circuit_model_design_tech& mem_tech, + const ModuleManager::e_config_child_type& config_child_type) { switch (mem_tech) { case CIRCUIT_MODEL_DESIGN_CMOS: add_module_nets_cmos_memory_config_bus(module_manager, decoder_lib, - parent_module, sram_orgz_type); + parent_module, sram_orgz_type, config_child_type); break; case CIRCUIT_MODEL_DESIGN_RRAM: /* TODO: */ @@ -1911,11 +1924,12 @@ void add_module_nets_memory_config_bus( void add_pb_module_nets_memory_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_design_tech& mem_tech) { + const e_circuit_model_design_tech& mem_tech, + const ModuleManager::e_config_child_type& config_child_type) { switch (mem_tech) { case CIRCUIT_MODEL_DESIGN_CMOS: add_pb_module_nets_cmos_memory_config_bus(module_manager, decoder_lib, - parent_module, sram_orgz_type); + parent_module, sram_orgz_type, config_child_type); break; case CIRCUIT_MODEL_DESIGN_RRAM: /* TODO: */ @@ -2366,7 +2380,8 @@ size_t find_module_num_shared_config_bits_from_child_modules( size_t find_module_num_config_bits_from_child_modules( ModuleManager& module_manager, const ModuleId& module_id, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { size_t num_config_bits = 0; switch (sram_orgz_type) { @@ -2380,7 +2395,7 @@ size_t find_module_num_config_bits_from_child_modules( * per configurable children */ for (const ModuleId& child : - module_manager.configurable_children(module_id)) { + module_manager.configurable_children(module_id, config_child_type)) { num_config_bits += find_module_num_config_bits( module_manager, child, circuit_lib, sram_model, sram_orgz_type); } @@ -2393,7 +2408,7 @@ size_t find_module_num_config_bits_from_child_modules( * - and the number of configurable children */ for (const ModuleId& child : - module_manager.configurable_children(module_id)) { + module_manager.configurable_children(module_id, config_child_type)) { size_t temp_num_config_bits = find_module_num_config_bits( module_manager, child, circuit_lib, sram_model, sram_orgz_type); num_config_bits = @@ -2403,9 +2418,9 @@ size_t find_module_num_config_bits_from_child_modules( /* If there are more than 2 configurable children, we need a decoder * Otherwise, we can just short wire the address port to the children */ - if (1 < module_manager.configurable_children(module_id).size()) { + if (1 < module_manager.num_configurable_children(module_id, config_child_type)) { num_config_bits += find_mux_local_decoder_addr_size( - module_manager.configurable_children(module_id).size()); + module_manager.num_configurable_children(module_id, config_child_type)); } break; diff --git a/openfpga/src/utils/module_manager_utils.h b/openfpga/src/utils/module_manager_utils.h index 8a3566df9..c9d42235a 100644 --- a/openfpga/src/utils/module_manager_utils.h +++ b/openfpga/src/utils/module_manager_utils.h @@ -110,7 +110,8 @@ void add_module_nets_between_logic_and_memory_sram_bus( void add_module_nets_cmos_flatten_memory_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_port_type& config_port_type); + const e_circuit_model_port_type& config_port_type, + const ModuleManager::e_config_child_type& config_child_type); void add_module_nets_cmos_memory_bank_bl_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, @@ -124,21 +125,25 @@ void add_module_nets_cmos_memory_bank_wl_config_bus( void add_module_nets_cmos_memory_chain_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, - const e_config_protocol_type& sram_orgz_type); + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type); void add_module_nets_cmos_memory_frame_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, - const ModuleId& parent_module); + const ModuleId& parent_module, + const ModuleManager::e_config_child_type& config_child_type); void add_module_nets_memory_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_design_tech& mem_tech); + const e_circuit_model_design_tech& mem_tech, + const ModuleManager::e_config_child_type& config_child_type); void add_pb_module_nets_memory_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_design_tech& mem_tech); + const e_circuit_model_design_tech& mem_tech, + const ModuleManager::e_config_child_type& config_child_type); size_t find_module_num_shared_config_bits(const ModuleManager& module_manager, const ModuleId& module_id); @@ -146,7 +151,8 @@ size_t find_module_num_shared_config_bits(const ModuleManager& module_manager, size_t find_module_num_config_bits( const ModuleManager& module_manager, const ModuleId& module_id, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, - const e_config_protocol_type& sram_orgz_type); + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type); void add_module_global_input_ports_from_child_modules( ModuleManager& module_manager, const ModuleId& module_id, From 2facde2097f67c4f525cba2996c7c1fecc125073 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 12:57:50 -0700 Subject: [PATCH 10/41] [core] reworked fabric generator to use config child type --- openfpga/src/fabric/build_memory_modules.cpp | 53 +++++++------- openfpga/src/fabric/build_routing_modules.cpp | 26 ++++--- openfpga/src/fabric/build_tile_modules.cpp | 12 ++-- openfpga/src/fabric/build_top_module.cpp | 2 +- .../build_top_module_child_tile_instance.cpp | 3 +- .../src/fabric/build_top_module_memory.cpp | 34 ++++----- .../src/utils/module_manager_memory_utils.cpp | 71 ++++++++++--------- 7 files changed, 108 insertions(+), 93 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 4b5a9616b..64828066a 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -173,7 +173,7 @@ static void add_module_nets_to_cmos_memory_config_chain_module( const CircuitLibrary& circuit_lib, const CircuitPortId& model_input_port, const CircuitPortId& model_output_port) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL).size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -194,27 +194,27 @@ static void add_module_nets_to_cmos_memory_config_chain_module( /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); net_src_module_id = - module_manager.configurable_children(parent_module)[mem_index - 1]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[mem_index - 1]; + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -248,9 +248,9 @@ static void add_module_nets_to_cmos_memory_config_chain_module( /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module).back(); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL).back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module).back(); + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL).back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -310,7 +310,7 @@ static void add_module_nets_to_cmos_memory_scan_chain_module( const CircuitLibrary& circuit_lib, const CircuitPortId& model_input_port, const CircuitPortId& model_output_port) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL).size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -331,16 +331,16 @@ static void add_module_nets_to_cmos_memory_scan_chain_module( /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); net_src_module_id = - module_manager.configurable_children(parent_module)[mem_index - 1]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( parent_module)[mem_index - 1]; net_src_port_id = @@ -349,9 +349,9 @@ static void add_module_nets_to_cmos_memory_scan_chain_module( /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -480,7 +480,7 @@ static void build_memory_flatten_module(ModuleManager& module_manager, module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance); + sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); /* Build module nets */ /* Wire inputs of parent module to inputs of child modules */ @@ -613,7 +613,7 @@ static void build_memory_chain_module(ModuleManager& module_manager, module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance); + sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); /* Build module nets to wire outputs of sram modules to outputs of memory * module */ @@ -830,7 +830,7 @@ static void build_frame_memory_module(ModuleManager& module_manager, module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_instance); + sram_instance, ModuleManager::e_config_child_type::UNIFIED); /* Wire data_in port to SRAM BL port */ ModulePortId sram_bl_port = module_manager.find_module_port( @@ -890,7 +890,7 @@ static void build_frame_memory_module(ModuleManager& module_manager, add_module_global_ports_from_child_modules(module_manager, mem_module); /* Add the decoder as the last configurable children */ - module_manager.add_configurable_child(mem_module, decoder_module, 0); + module_manager.add_configurable_child(mem_module, decoder_module, 0, ModuleManager::e_config_child_type::UNIFIED); } /********************************************************************* @@ -1292,7 +1292,7 @@ int build_memory_group_module(ModuleManager& module_manager, ModuleId child_module = child_modules[ichild]; size_t child_instance = module_manager.num_instance(mem_module, child_module); module_manager.add_child_module(mem_module, child_module, false); - module_manager.add_configurable_child(mem_module, child_module, child_instance, false); + module_manager.add_configurable_child(mem_module, child_module, child_instance, ModuleManager::e_config_child_type::UNIFIED); /* Wire outputs of child module to outputs of parent module */ add_module_output_nets_to_memory_group_module( module_manager, mem_module, out_port_name, @@ -1342,9 +1342,10 @@ int build_memory_group_module(ModuleManager& module_manager, * we just need to find all the I/O ports from the child modules and build a * list of it */ + ModuleManager::e_config_child_type config_child_type = ModuleManager::e_config_child_type::PHYSICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type); + module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); if (0 < module_num_config_bits) { add_sram_ports_to_module_manager(module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type, @@ -1355,10 +1356,10 @@ int build_memory_group_module(ModuleManager& module_manager, * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.configurable_children(mem_module).size()) { + if (0 < module_manager.num_configurable_children(mem_module, config_child_type)) { add_module_nets_memory_config_bus(module_manager, decoder_lib, mem_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model)); + circuit_lib.design_tech_type(sram_model), config_child_type); } return CMD_EXEC_SUCCESS; @@ -1417,7 +1418,7 @@ int add_physical_memory_module(ModuleManager& module_manager, std::map mem2mem_port_map; mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); - for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) { + for (size_t ichild = 0; ichild < module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++ichild) { for (CircuitPortType port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { std::string src_port_name = mem2mem_port_map[port_type]; std::string des_port_name = @@ -1431,8 +1432,8 @@ int add_physical_memory_module(ModuleManager& module_manager, BasicPort src_port = module_manager.module_port(phy_mem_module, src_port_id); - ModuleId des_module = module_manager.logical_configurable_children(curr_module)[ichild]; - size_t des_instance = module_manager.logical_configurable_child_instances(curr_module)[ichild]; + ModuleId des_module = module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + size_t des_instance = module_manager.configurable_child_instances(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; ModulePortId des_port_id = module_manager.find_module_port(des_module, des_port_name); if (!module_manager.valid_module_port_id(des_module, des_port_id)) { diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index b889f86fe..8430cb6e8 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -241,15 +241,16 @@ static void build_switch_block_mux_module( module_manager, sb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ - size_t config_child_id = module_manager.num_configurable_children(sb_module); - module_manager.add_configurable_child(sb_module, mem_module, mem_instance_id, group_config_block); + size_t config_child_id = module_manager.num_configurable_children(sb_module, ModuleManager::e_config_child_type::LOGICAL); + module_manager.add_configurable_child(sb_module, mem_module, mem_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); /* For logical memory, define the physical memory here */ if (group_config_block) { std::string physical_mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); ModuleId physical_mem_module = module_manager.find_module(physical_mem_module_name); - module_manager.set_physical_configurable_child(sb_module, config_child_id, physical_mem_module); + VTR_ASSERT(true == module_manager.valid_module_id(physical_mem_module)); + module_manager.set_logical2physical_configurable_child(sb_module, config_child_id, physical_mem_module); } } @@ -509,9 +510,10 @@ static void build_switch_block_module( * we just need to find all the I/O ports from the child modules and build a * list of it */ + ModuleManager::e_config_child_type config_child_type = group_config_block ? ModuleManager::e_config_child_type::PHYSICAL : ModuleManager::e_config_child_type::LOGICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type); + module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type, @@ -522,10 +524,10 @@ static void build_switch_block_module( * primitive modules This is a one-shot addition that covers all the memory * modules in this primitive module! */ - if (0 < module_manager.configurable_children(sb_module).size()) { + if (0 < module_manager.num_configurable_children(sb_module, config_child_type)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, sb_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model)); + circuit_lib.design_tech_type(sram_model), config_child_type); } VTR_LOGV(verbose, "Done\n"); @@ -751,14 +753,15 @@ static void build_connection_block_mux_module( mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ size_t config_child_id = module_manager.num_configurable_children(cb_module); - module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id, group_config_block); + module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); /* For logical memory, define the physical memory here */ if (group_config_block) { std::string physical_mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); ModuleId physical_mem_module = module_manager.find_module(physical_mem_module_name); - module_manager.set_physical_configurable_child(cb_module, config_child_id, physical_mem_module); + VTR_ASSERT(true == module_manager.valid_module_id(physical_mem_module)); + module_manager.set_logical2physical_configurable_child(cb_module, config_child_id, physical_mem_module); } } @@ -1016,9 +1019,10 @@ static void build_connection_block_module( * we just need to find all the I/O ports from the child modules and build a * list of it */ + ModuleManager::e_config_child_type config_child_type = group_config_block ? ModuleManager::e_config_child_type::PHYSICAL : ModuleManager::e_config_child_type::LOGICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type); + module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type, @@ -1029,10 +1033,10 @@ static void build_connection_block_module( * primitive modules This is a one-shot addition that covers all the memory * modules in this primitive module! */ - if (0 < module_manager.configurable_children(cb_module).size()) { + if (0 < module_manager.num_configurable_children(cb_module, config_child_type)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, cb_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model)); + circuit_lib.design_tech_type(sram_model), config_child_type); } VTR_LOGV(verbose, "Done\n"); diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index c3c209f62..43923ef91 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1332,7 +1332,7 @@ static int build_tile_module( circuit_lib, sram_model, sram_orgz_type)) { module_manager.add_configurable_child(tile_module, pb_module, - pb_instance); + pb_instance, ModuleManager::e_config_child_type::UNIFIED); } VTR_LOGV( verbose, @@ -1379,7 +1379,7 @@ static int build_tile_module( circuit_lib, sram_model, sram_orgz_type)) { module_manager.add_configurable_child(tile_module, cb_module, - cb_instance); + cb_instance, ModuleManager::e_config_child_type::UNIFIED); } VTR_LOGV(verbose, "Added connection block module '%s' (instance: '%s') to " @@ -1418,7 +1418,7 @@ static int build_tile_module( if (0 < find_module_num_config_bits(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type)) { module_manager.add_configurable_child(tile_module, sb_module, - sb_instance); + sb_instance, ModuleManager::e_config_child_type::UNIFIED); } VTR_LOGV( verbose, @@ -1468,7 +1468,7 @@ static int build_tile_module( */ size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, tile_module, circuit_lib, sram_model, sram_orgz_type); + module_manager, tile_module, circuit_lib, sram_model, sram_orgz_type, ModuleManager::e_config_child_type::LOGICAL); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, tile_module, circuit_lib, sram_model, sram_orgz_type, @@ -1479,10 +1479,10 @@ static int build_tile_module( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.logical_configurable_children(tile_module).size()) { + if (0 < module_manager.num_configurable_children(tile_module, ModuleManager::e_config_child_type::LOGICAL)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, tile_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model)); + circuit_lib.design_tech_type(sram_model), ModuleManager::e_config_child_type::LOGICAL); } VTR_LOGV(verbose, "Done\n"); diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index cb98ed8dd..d8771f5de 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -134,7 +134,7 @@ int build_top_module( * module! */ if (false == frame_view) { - if (0 < module_manager.configurable_children(top_module).size()) { + if (0 < module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size()) { add_top_module_nets_memory_config_bus( module_manager, decoder_lib, blwl_sr_banks, top_module, circuit_lib, config_protocol, circuit_lib.design_tech_type(sram_model), diff --git a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp index acd844766..d32e8ac81 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1076,7 +1076,7 @@ static void organize_top_module_tile_based_memory_modules( const CircuitModelId& sram_model, const DeviceGrid& grids, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { /* Ensure clean vectors to return */ - VTR_ASSERT(true == module_manager.configurable_children(top_module).empty()); + VTR_ASSERT(true == module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).empty()); std::vector> tile_coords; bool positive_direction = true; @@ -1116,6 +1116,7 @@ static void organize_top_module_tile_based_memory_modules( module_manager.add_configurable_child( top_module, tile_module, tile_instance_ids[curr_tile_coord.x()][curr_tile_coord.y()], + ModuleManager::e_config_child_type::UNIFIED, vtr::Point(curr_tile_coord.x(), curr_tile_coord.y())); } } diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 5d6ce081c..a8d279ea6 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -86,7 +86,7 @@ static void organize_top_module_tile_cb_modules( module_manager.add_configurable_child( top_module, cb_module, cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)], - false, + ModuleManager::e_config_child_type::UNIFIED, config_coord); } } @@ -174,7 +174,7 @@ static void organize_top_module_tile_memory_modules( rr_gsb.get_sb_y() * 2 + 1); module_manager.add_configurable_child( top_module, sb_module, - sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], false, config_coord); + sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], ModuleManager::e_config_child_type::UNIFIED, config_coord); } } @@ -219,7 +219,8 @@ static void organize_top_module_tile_memory_modules( sram_model, sram_orgz_type)) { vtr::Point config_coord(tile_coord.x() * 2, tile_coord.y() * 2); module_manager.add_configurable_child( - top_module, grid_module, false, + top_module, grid_module, + ModuleManager::e_config_child_type::UNIFIED, grid_instance_ids[tile_coord.x()][tile_coord.y()], config_coord); } } @@ -270,14 +271,14 @@ void build_top_module_configurable_regions( "Build configurable regions for the top module"); /* Ensure we have valid configurable children */ - VTR_ASSERT(false == module_manager.logical_configurable_children(top_module).empty()); + VTR_ASSERT(false == module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).empty()); /* Ensure that our region definition is valid */ VTR_ASSERT(1 <= config_protocol.num_regions()); /* Exclude decoders from the list */ size_t num_configurable_children = - module_manager.logical_configurable_children(top_module).size(); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); if (CONFIG_MEM_MEMORY_BANK == config_protocol.type() || CONFIG_MEM_QL_MEMORY_BANK == config_protocol.type()) { num_configurable_children -= 2; @@ -292,7 +293,7 @@ void build_top_module_configurable_regions( bool create_region = true; ConfigRegionId curr_region = ConfigRegionId::INVALID(); for (size_t ichild = 0; - ichild < module_manager.logical_configurable_children(top_module).size(); + ichild < module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++ichild) { if (true == create_region) { curr_region = module_manager.add_config_region(top_module); @@ -301,8 +302,8 @@ void build_top_module_configurable_regions( /* Add the child to a region */ module_manager.add_configurable_child_to_region( top_module, curr_region, - module_manager.logical_configurable_children(top_module)[ichild], - module_manager.logical_configurable_child_instances(top_module)[ichild], ichild); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild], + module_manager.configurable_child_instances(top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild], ichild); /* See if the current region is full or not: * For the last region, we will keep adding until we finish all the children @@ -531,7 +532,7 @@ void organize_top_module_memory_modules( void shuffle_top_module_configurable_children( ModuleManager& module_manager, const ModuleId& top_module, const ConfigProtocol& config_protocol) { - size_t num_keys = module_manager.configurable_children(top_module).size(); + size_t num_keys = module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); std::vector shuffled_keys; shuffled_keys.reserve(num_keys); for (size_t ikey = 0; ikey < num_keys; ++ikey) { @@ -542,11 +543,11 @@ void shuffle_top_module_configurable_children( /* Cache the configurable children and their instances */ std::vector orig_configurable_children = - module_manager.logical_configurable_children(top_module); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL); std::vector orig_configurable_child_instances = - module_manager.logical_configurable_child_instances(top_module); + module_manager.configurable_child_instances(top_module, ModuleManager::e_config_child_type::PHYSICAL); std::vector> orig_configurable_child_coordinates = - module_manager.logical_configurable_child_coordinates(top_module); + module_manager.configurable_child_coordinates(top_module, ModuleManager::e_config_child_type::PHYSICAL); /* Reorganize the configurable children */ module_manager.clear_configurable_children(top_module); @@ -555,7 +556,7 @@ void shuffle_top_module_configurable_children( module_manager.add_configurable_child( top_module, orig_configurable_children[shuffled_keys[ikey]], orig_configurable_child_instances[shuffled_keys[ikey]], - false, + ModuleManager::e_config_child_type::UNIFIED, orig_configurable_child_coordinates[shuffled_keys[ikey]]); } @@ -653,7 +654,8 @@ int load_top_module_memory_modules_from_fabric_key( /* Now we can add the child to configurable children of the top module */ module_manager.add_configurable_child(top_module, instance_info.first, - instance_info.second, false, + instance_info.second, + ModuleManager::e_config_child_type::UNIFIED, fabric_key.key_coordinate(key)); module_manager.add_configurable_child_to_region( top_module, top_module_config_region, instance_info.first, @@ -1929,10 +1931,10 @@ static void add_top_module_nets_cmos_memory_config_bus( case CONFIG_MEM_STANDALONE: add_module_nets_cmos_flatten_memory_config_bus( module_manager, parent_module, config_protocol.type(), - CIRCUIT_MODEL_PORT_BL); + CIRCUIT_MODEL_PORT_BL, ModuleManager::e_config_child_type::PHYSICAL); add_module_nets_cmos_flatten_memory_config_bus( module_manager, parent_module, config_protocol.type(), - CIRCUIT_MODEL_PORT_WL); + CIRCUIT_MODEL_PORT_WL, ModuleManager::e_config_child_type::PHYSICAL); break; case CONFIG_MEM_SCAN_CHAIN: { add_top_module_nets_cmos_memory_chain_config_bus( diff --git a/openfpga/src/utils/module_manager_memory_utils.cpp b/openfpga/src/utils/module_manager_memory_utils.cpp index 918b27e8f..5e6ca8701 100644 --- a/openfpga/src/utils/module_manager_memory_utils.cpp +++ b/openfpga/src/utils/module_manager_memory_utils.cpp @@ -39,7 +39,7 @@ static bool submodule_memory_modules_match_fabric_key( const FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { /* If the length does not match, conclusion is easy to be made */ size_t len_module_memory = - module_manager.configurable_children(module_id).size(); + module_manager.configurable_children(module_id, ModuleManager::e_config_child_type::PHYSICAL).size(); size_t len_fabric_sub_key = fabric_key.sub_keys(key_module_id).size(); if (len_module_memory != len_fabric_sub_key) { return false; @@ -65,9 +65,9 @@ static bool submodule_memory_modules_match_fabric_key( inst_info.second = fabric_key.sub_key_value(key_id); } if (inst_info.first != - module_manager.configurable_children(module_id)[ikey] || + module_manager.configurable_children(module_id, ModuleManager::e_config_child_type::PHYSICAL)[ikey] || inst_info.second != - module_manager.configurable_child_instances(module_id)[ikey]) { + module_manager.configurable_child_instances(module_id, ModuleManager::e_config_child_type::PHYSICAL)[ikey]) { return false; } } @@ -80,9 +80,10 @@ static bool submodule_memory_modules_match_fabric_key( static bool update_submodule_memory_modules_from_fabric_key( ModuleManager& module_manager, const ModuleId& module_id, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, + const ModuleManager::e_config_child_type& config_child_type, const FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { /* Reset the configurable children */ - module_manager.clear_configurable_children(module_id); + module_manager.clear_configurable_children(module_id, config_child_type); for (FabricSubKeyId key_id : fabric_key.sub_keys(key_module_id)) { std::pair inst_info(ModuleId::INVALID(), 0); @@ -142,7 +143,7 @@ static bool update_submodule_memory_modules_from_fabric_key( /* Now we can add the child to configurable children of the top module */ module_manager.add_configurable_child(module_id, inst_info.first, - inst_info.second, vtr::Point()); + inst_info.second, config_child_type, vtr::Point()); } return CMD_EXEC_SUCCESS; } @@ -152,9 +153,10 @@ static bool update_submodule_memory_modules_from_fabric_key( *******************************************************************/ static int remove_submodule_nets_cmos_memory_chain_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -172,9 +174,9 @@ static int remove_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); net_src_module_id = - module_manager.configurable_children(parent_module)[mem_index - 1]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[mem_index - 1]; + parent_module, config_child_type)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); } @@ -201,9 +203,9 @@ static int remove_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module).back(); + module_manager.configurable_children(parent_module, config_child_type).back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module).back(); + module_manager.configurable_child_instances(parent_module, config_child_type).back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -229,11 +231,12 @@ static int remove_submodule_nets_cmos_memory_chain_config_bus( *******************************************************************/ static int remove_submodule_nets_cmos_memory_config_bus( ModuleManager& module_manager, const ModuleId& module_id, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { switch (sram_orgz_type) { case CONFIG_MEM_SCAN_CHAIN: { return remove_submodule_nets_cmos_memory_chain_config_bus( - module_manager, module_id, sram_orgz_type); + module_manager, module_id, sram_orgz_type, config_child_type); break; } case CONFIG_MEM_STANDALONE: @@ -274,11 +277,12 @@ static int remove_submodule_nets_cmos_memory_config_bus( *******************************************************************/ static int remove_submodule_configurable_children_nets( ModuleManager& module_manager, const ModuleId& module_id, - const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, + const ModuleManager::e_config_child_type& config_child_type) { switch (circuit_lib.design_tech_type(config_protocol.memory_model())) { case CIRCUIT_MODEL_DESIGN_CMOS: return remove_submodule_nets_cmos_memory_config_bus( - module_manager, module_id, config_protocol.type()); + module_manager, module_id, config_protocol.type(), config_child_type); break; case CIRCUIT_MODEL_DESIGN_RRAM: /* TODO: */ @@ -297,9 +301,10 @@ static int remove_submodule_configurable_children_nets( *******************************************************************/ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -321,27 +326,27 @@ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); net_src_module_id = - module_manager.configurable_children(parent_module)[mem_index - 1]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[mem_index - 1]; + parent_module, config_child_type)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -375,9 +380,9 @@ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module).back(); + module_manager.configurable_children(parent_module, config_child_type).back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module).back(); + module_manager.configurable_child_instances(parent_module, config_child_type).back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -418,11 +423,12 @@ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( *******************************************************************/ static int rebuild_submodule_nets_cmos_memory_config_bus( ModuleManager& module_manager, const ModuleId& module_id, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type) { switch (sram_orgz_type) { case CONFIG_MEM_SCAN_CHAIN: { return rebuild_submodule_nets_cmos_memory_chain_config_bus( - module_manager, module_id, sram_orgz_type); + module_manager, module_id, sram_orgz_type, config_child_type); break; } case CONFIG_MEM_STANDALONE: @@ -464,11 +470,12 @@ static int rebuild_submodule_nets_cmos_memory_config_bus( *******************************************************************/ static int rebuild_submodule_configurable_children_nets( ModuleManager& module_manager, const ModuleId& module_id, - const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, + const ModuleManager::e_config_child_type& config_child_type) { switch (circuit_lib.design_tech_type(config_protocol.memory_model())) { case CIRCUIT_MODEL_DESIGN_CMOS: return rebuild_submodule_nets_cmos_memory_config_bus( - module_manager, module_id, config_protocol.type()); + module_manager, module_id, config_protocol.type(), config_child_type); break; case CIRCUIT_MODEL_DESIGN_RRAM: /* TODO: */ @@ -502,20 +509,20 @@ static int load_and_update_submodule_memory_modules_from_fabric_key( } /* Do not match, now remove all the nets for the configurable children */ status = remove_submodule_configurable_children_nets( - module_manager, module_id, circuit_lib, config_protocol); + module_manager, module_id, circuit_lib, config_protocol, ModuleManager::e_config_child_type::PHYSICAL); if (status == CMD_EXEC_FATAL_ERROR) { return status; } /* Overwrite the configurable children list */ status = update_submodule_memory_modules_from_fabric_key( - module_manager, module_id, circuit_lib, config_protocol, fabric_key, + module_manager, module_id, circuit_lib, config_protocol, ModuleManager::e_config_child_type::PHYSICAL, fabric_key, key_module_id); if (status == CMD_EXEC_FATAL_ERROR) { return status; } /* TODO: Create the nets for the new list of configurable children */ status = rebuild_submodule_configurable_children_nets( - module_manager, module_id, circuit_lib, config_protocol); + module_manager, module_id, circuit_lib, config_protocol, ModuleManager::e_config_child_type::PHYSICAL); if (status == CMD_EXEC_FATAL_ERROR) { return status; } From 3331540ed6a7874c7cccde8ae38ed5d08714bd40 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 14:24:22 -0700 Subject: [PATCH 11/41] [core] using config child type in bitstream generation --- .../src/fabric/build_fpga_core_wrapper_module.cpp | 2 +- openfpga/src/fabric/build_memory_modules.cpp | 4 ++++ .../src/fpga_bitstream/build_device_bitstream.cpp | 14 +++++++------- .../src/fpga_bitstream/build_grid_bitstream.cpp | 8 ++++---- openfpga/src/utils/module_manager_utils.cpp | 6 +++--- openfpga/src/utils/module_manager_utils.h | 2 +- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index 0be788f48..d79260583 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -381,7 +381,7 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, /* Now fpga_core should be the only configurable child under the top-level * module */ - module_manager.add_configurable_child(new_top_module, top_module, 0, false); + module_manager.add_configurable_child(new_top_module, top_module, 0, ModuleManager::e_config_child_type::UNIFIED); return status; } diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 64828066a..f2d8d4bd3 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1392,6 +1392,10 @@ int add_physical_memory_module(ModuleManager& module_manager, size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH); + /* No need to build a memory when there are no configuration bits required */ + if (module_num_config_bits == 0) { + return CMD_EXEC_SUCCESS; + } std::string phy_mem_module_name = generate_physical_memory_module_name(module_manager.module_name(curr_module), module_num_config_bits); ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); if (!module_manager.valid_module_id(phy_mem_module)) { diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 98c3ce4e1..9e3d721f4 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -35,15 +35,15 @@ static size_t rec_estimate_device_bitstream_num_blocks( * actually configurable memory elements * We skip them in couting */ - if (0 == module_manager.logical_configurable_children(top_module).size()) { + if (0 == module_manager.num_configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL)) { return 0; } size_t num_configurable_children = - module_manager.logical_configurable_children(top_module).size(); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); for (size_t ichild = 0; ichild < num_configurable_children; ++ichild) { ModuleId child_module = - module_manager.logical_configurable_children(top_module)[ichild]; + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; num_blocks += rec_estimate_device_bitstream_num_blocks(module_manager, child_module); } @@ -68,7 +68,7 @@ static size_t rec_estimate_device_bitstream_num_bits( /* 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.logical_configurable_children(parent_module).size()) { + if (0 == module_manager.num_configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)) { return 1; } @@ -105,7 +105,7 @@ static size_t rec_estimate_device_bitstream_num_bits( VTR_ASSERT_SAFE(parent_module != top_module); size_t num_configurable_children = - module_manager.logical_configurable_children(parent_module).size(); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); /* Frame-based configuration protocol will have 1 decoder * if there are more than 1 configurable children @@ -117,7 +117,7 @@ static size_t rec_estimate_device_bitstream_num_bits( for (size_t ichild = 0; ichild < num_configurable_children; ++ichild) { ModuleId child_module = - module_manager.logical_configurable_children(parent_module)[ichild]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; num_bits += rec_estimate_device_bitstream_num_bits( module_manager, top_module, child_module, config_protocol); } @@ -193,7 +193,7 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, /* 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)); + openfpga_ctx.module_graph(), top_module), ModuleManager::e_config_child_type::PHYSICAL); /* Create bitstream from grids */ VTR_LOGV(verbose, "Building grid bitstream...\n"); diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 3f642b5fc..1caee205b 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -636,7 +636,7 @@ static void rec_build_physical_block_bitstream( VTR_ASSERT(true == module_manager.valid_module_id(pb_module)); /* Skip module with no configurable children */ - if (0 == module_manager.logical_configurable_children(pb_module).size()) { + if (0 == module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::LOGICAL)) { return; } @@ -653,7 +653,7 @@ static void rec_build_physical_block_bitstream( bitstream_manager.reserve_child_blocks( parent_configurable_block, count_module_manager_module_configurable_children(module_manager, - pb_module)); + pb_module, ModuleManager::e_config_child_type::PHYSICAL)); /* Recursively finish all the child pb_types*/ if (false == is_primitive_pb_type(physical_pb_type)) { @@ -748,7 +748,7 @@ static void build_physical_block_bitstream( VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); /* Skip module with no configurable children */ - if (0 == module_manager.configurable_children(grid_module).size()) { + if (0 == module_manager.num_configurable_children(grid_module, ModuleManager::e_config_child_type::LOGICAL)) { return; } @@ -770,7 +770,7 @@ static void build_physical_block_bitstream( /* Reserve child blocks for new created block */ bitstream_manager.reserve_child_blocks( grid_configurable_block, count_module_manager_module_configurable_children( - module_manager, grid_module)); + module_manager, grid_module, ModuleManager::e_config_child_type::PHYSICAL)); /* Iterate over the capacity of the grid * Now each physical tile may have a number of logical blocks diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 63252791f..87baecf78 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -86,11 +86,11 @@ void reserve_module_manager_module_nets(ModuleManager& module_manager, *children as well ******************************************************************************/ size_t count_module_manager_module_configurable_children( - const ModuleManager& module_manager, const ModuleId& module) { + const ModuleManager& module_manager, const ModuleId& module, const ModuleManager::e_config_child_type& config_child_type) { size_t num_config_children = 0; - for (const ModuleId& child : module_manager.logical_configurable_children(module)) { - if (0 != module_manager.logical_configurable_children(child).size()) { + for (const ModuleId& child : module_manager.configurable_children(module, config_child_type)) { + if (0 != module_manager.configurable_children(child, config_child_type).size()) { num_config_children++; } } diff --git a/openfpga/src/utils/module_manager_utils.h b/openfpga/src/utils/module_manager_utils.h index c9d42235a..b1e02d79f 100644 --- a/openfpga/src/utils/module_manager_utils.h +++ b/openfpga/src/utils/module_manager_utils.h @@ -42,7 +42,7 @@ void reserve_module_manager_module_nets(ModuleManager& module_manager, const ModuleId& module); size_t count_module_manager_module_configurable_children( - const ModuleManager& module_manager, const ModuleId& module); + const ModuleManager& module_manager, const ModuleId& module, const ModuleManager::e_config_child_type& config_child_type); std::pair find_module_manager_instance_module_info( const ModuleManager& module_manager, const ModuleId& parent, From 5618f1d5672c08cebe3760c752c43bc30dd2f8eb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 16:06:19 -0700 Subject: [PATCH 12/41] [core] now bitgen uses config child types --- openfpga/src/fabric/module_manager.cpp | 17 +++++++++- openfpga/src/fabric/module_manager.h | 3 ++ .../fpga_bitstream/build_fabric_bitstream.cpp | 20 ++++++------ .../build_fabric_bitstream_memory_bank.cpp | 4 +-- .../fpga_bitstream/build_grid_bitstream.cpp | 31 +++++++++++++------ .../build_routing_bitstream.cpp | 26 +++++++++++++--- 6 files changed, 74 insertions(+), 27 deletions(-) diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index ca36a9b9f..72cd67109 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -241,7 +241,7 @@ ModuleManager::region_configurable_child_coordinates( for (const size_t& child_id : config_region_children_[parent_module][region]) { region_config_child_coordinates.push_back( - logical_configurable_child_coordinates_[parent_module][child_id]); + physical_configurable_child_coordinates_[parent_module][child_id]); } return region_config_child_coordinates; @@ -657,6 +657,21 @@ bool ModuleManager::net_sink_exist(const ModuleId& module, return false; } +bool ModuleManager::unified_configurable_children(const ModuleId& curr_module) const { + if (logical_configurable_children_[curr_module].size() != physical_configurable_children_[curr_module].size()) { + return false; + } + for (size_t ichild = 0; ichild < logical_configurable_children_[curr_module].size(); ++ichild) { + if (logical_configurable_children_[curr_module][ichild] != physical_configurable_children_[curr_module][ichild]) { + return false; + } + if (logical_configurable_child_instances_[curr_module][ichild] != physical_configurable_child_instances_[curr_module][ichild]) { + return false; + } + } + return true; +} + /****************************************************************************** * Private Accessors ******************************************************************************/ diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index f57f62361..2c6f1fc7c 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -323,6 +323,9 @@ class ModuleManager { const ModuleId& sink_module, const size_t& instance_id, const ModulePortId& sink_port, const size_t& sink_pin); + /** @brief Check if the configurable children under a given module are unified or not. If unified, it means that the logical configurable children are the same as the physical configurable children */ + bool unified_configurable_children(const ModuleId& curr_module) const; + private: /* Private accessors */ size_t find_child_module_index_in_parent_module( const ModuleId& parent_module, const ModuleId& child_module) const; diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index ddd51c053..4a9d17406 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -74,12 +74,12 @@ static void rec_build_module_fabric_dependent_chain_bitstream( } else { for (size_t child_id = 0; child_id < - module_manager.logical_configurable_children(parent_module).size(); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++child_id) { ModuleId child_module = - module_manager.logical_configurable_children(parent_module)[child_id]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_id]; size_t child_instance = - module_manager.logical_configurable_child_instances(parent_module)[child_id]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[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); @@ -196,7 +196,7 @@ static void rec_build_module_fabric_dependent_memory_bank_bitstream( * - no need to exclude decoders as they are not there */ std::vector configurable_children = - module_manager.logical_configurable_children(parent_module); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL); size_t num_configurable_children = configurable_children.size(); @@ -212,7 +212,7 @@ static void rec_build_module_fabric_dependent_memory_bank_bitstream( ++child_id) { ModuleId child_module = configurable_children[child_id]; size_t child_instance = - module_manager.logical_configurable_child_instances(parent_module)[child_id]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_id]; /* Get the instance name and ensure it is not empty */ std::string instance_name = module_manager.instance_name( @@ -324,9 +324,9 @@ static void rec_build_module_fabric_dependent_frame_bitstream( } else { VTR_ASSERT(top_module != parent_module); configurable_children = - module_manager.logical_configurable_children(parent_module); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL); configurable_child_instances = - module_manager.logical_configurable_child_instances(parent_module); + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL); } size_t num_configurable_children = configurable_children.size(); @@ -361,9 +361,9 @@ static void rec_build_module_fabric_dependent_frame_bitstream( * configurable children in all the regions */ for (const ModuleId& child_module : - module_manager.logical_configurable_children(parent_module)) { + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)) { /* Bypass any decoder module (which no configurable children */ - if (module_manager.logical_configurable_children(child_module).empty()) { + if (module_manager.configurable_children(child_module, ModuleManager::e_config_child_type::PHYSICAL).empty()) { continue; } const ModulePortId& child_addr_port_id = @@ -494,7 +494,7 @@ static void rec_build_module_fabric_dependent_frame_bitstream( } else { VTR_ASSERT(top_module != parent_modules.back()); configurable_children = - module_manager.logical_configurable_children(parent_modules.back()); + module_manager.configurable_children(parent_modules.back(), ModuleManager::e_config_child_type::PHYSICAL); } ModuleId decoder_module = configurable_children.back(); diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp index 183ed5975..a6e0efdac 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp @@ -123,7 +123,7 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( * - no need to exclude decoders as they are not there */ std::vector configurable_children = - module_manager.logical_configurable_children(parent_module); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL); size_t num_configurable_children = configurable_children.size(); @@ -139,7 +139,7 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( ++child_id) { ModuleId child_module = configurable_children[child_id]; size_t child_instance = - module_manager.logical_configurable_child_instances(parent_module)[child_id]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_id]; /* Get the instance name and ensure it is not empty */ std::string instance_name = module_manager.instance_name( diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 1caee205b..c1a4770f1 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -644,16 +644,18 @@ static void rec_build_physical_block_bitstream( * manager */ std::string pb_block_name = generate_physical_block_instance_name( physical_pb_type, pb_graph_node_index); - ConfigBlockId pb_configurable_block = - bitstream_manager.add_block(pb_block_name); - bitstream_manager.add_child_block(parent_configurable_block, - pb_configurable_block); - - /* Reserve child blocks for new created block */ - bitstream_manager.reserve_child_blocks( - parent_configurable_block, - count_module_manager_module_configurable_children(module_manager, - pb_module, ModuleManager::e_config_child_type::PHYSICAL)); + /* If there are no physical memory blocks under the current module, use the previous module, which is the physical memory block */ + ConfigBlockId pb_configurable_block = parent_configurable_block; + if (0 < module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::PHYSICAL)) { + pb_configurable_block = bitstream_manager.add_block(pb_block_name); + bitstream_manager.add_child_block(parent_configurable_block, + pb_configurable_block); + /* Reserve child blocks for new created block */ + bitstream_manager.reserve_child_blocks( + parent_configurable_block, + count_module_manager_module_configurable_children(module_manager, + pb_module, ModuleManager::e_config_child_type::PHYSICAL)); + } /* Recursively finish all the child pb_types*/ if (false == is_primitive_pb_type(physical_pb_type)) { @@ -772,6 +774,15 @@ static void build_physical_block_bitstream( grid_configurable_block, count_module_manager_module_configurable_children( module_manager, grid_module, ModuleManager::e_config_child_type::PHYSICAL)); + /* Create a dedicated block for the non-unified configurable child */ + if (!module_manager.unified_configurable_children(grid_module)) { + VTR_ASSERT(1 == module_manager.configurable_children(grid_module, ModuleManager::e_config_child_type::PHYSICAL).size()); + std::string phy_mem_instance_name = module_manager.instance_name(grid_module, module_manager.configurable_children(grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); + ConfigBlockId grid_grouped_config_block = bitstream_manager.add_child_block(phy_mem_instance_name); + bitstream_manager.add_child_block(grid_configurable_block, grid_grouped_config_block); + grid_configurable_block = grid_grouped_config_block; + } + /* Iterate over the capacity of the grid * Now each physical tile may have a number of logical blocks * OpenFPGA only considers the physical implementation of the tiles. diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index fda7d6765..006bb0998 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -487,7 +487,7 @@ static void build_connection_block_bitstreams( /* Bypass empty blocks which have none configurable children */ if (0 == count_module_manager_module_configurable_children(module_manager, - cb_module)) { + cb_module, ModuleManager::e_config_child_type::LOGICAL)) { continue; } @@ -528,7 +528,16 @@ static void build_connection_block_bitstreams( bitstream_manager.reserve_child_blocks( cb_configurable_block, count_module_manager_module_configurable_children(module_manager, - cb_module)); + cb_module, ModuleManager::e_config_child_type::PHYSICAL)); + + /* Create a dedicated block for the non-unified configurable child */ + if (!module_manager.unified_configurable_children(cb_module)) { + VTR_ASSERT(1 == module_manager.configurable_children(cb_module, ModuleManager::e_config_child_type::PHYSICAL).size()); + std::string phy_mem_instance_name = module_manager.instance_name(cb_module, module_manager.configurable_children(cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); + ConfigBlockId cb_grouped_config_block = bitstream_manager.add_child_block(phy_mem_instance_name); + bitstream_manager.add_child_block(cb_configurable_block, cb_grouped_config_block); + cb_configurable_block = cb_grouped_config_block; + } build_connection_block_bitstream( bitstream_manager, cb_configurable_block, module_manager, circuit_lib, @@ -594,7 +603,7 @@ void build_routing_bitstream( /* Bypass empty blocks which have none configurable children */ if (0 == count_module_manager_module_configurable_children(module_manager, - sb_module)) { + sb_module, ModuleManager::e_config_child_type::LOGICAL)) { continue; } @@ -630,7 +639,16 @@ void build_routing_bitstream( bitstream_manager.reserve_child_blocks( sb_configurable_block, count_module_manager_module_configurable_children(module_manager, - sb_module)); + sb_module, ModuleManager::e_config_child_type::PHYSICAL)); + + /* Create a dedicated block for the non-unified configurable child */ + if (!module_manager.unified_configurable_children(sb_module)) { + VTR_ASSERT(1 == module_manager.configurable_children(sb_module, ModuleManager::e_config_child_type::PHYSICAL).size()); + std::string phy_mem_instance_name = module_manager.instance_name(sb_module, module_manager.configurable_children(sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); + ConfigBlockId sb_grouped_config_block = bitstream_manager.add_child_block(phy_mem_instance_name); + bitstream_manager.add_child_block(sb_configurable_block, sb_grouped_config_block); + sb_configurable_block = sb_grouped_config_block; + } build_switch_block_bitstream(bitstream_manager, sb_configurable_block, module_manager, circuit_lib, mux_lib, From f4cbc9505387f71deb105749bf9e6a0b3aadd650 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 17:33:57 -0700 Subject: [PATCH 13/41] [core] syntax --- openfpga/src/fabric/build_grid_modules.cpp | 5 +-- openfpga/src/fabric/build_memory_modules.cpp | 18 +++++----- openfpga/src/fabric/build_memory_modules.h | 5 +-- openfpga/src/fabric/build_routing_modules.cpp | 7 ++-- .../src/fabric/build_top_module_memory.cpp | 21 ++++++------ .../fabric/build_top_module_memory_bank.cpp | 30 ++++++++-------- openfpga/src/fabric/fabric_key_writer.cpp | 10 +++--- openfpga/src/fabric/module_manager.cpp | 34 +++++++++++-------- .../fpga_bitstream/build_device_bitstream.cpp | 2 +- .../fpga_bitstream/build_grid_bitstream.cpp | 2 +- .../build_routing_bitstream.cpp | 4 +-- .../configuration_chain_sdc_writer.cpp | 8 ++--- openfpga/src/fpga_sdc/sdc_memory_utils.cpp | 8 ++--- openfpga/src/utils/memory_utils.cpp | 9 +++-- .../src/utils/module_manager_memory_utils.cpp | 2 +- openfpga/src/utils/module_manager_utils.cpp | 18 +++++----- openfpga/src/utils/module_manager_utils.h | 12 ++++--- 17 files changed, 103 insertions(+), 92 deletions(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 09b3566fa..358975fd5 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -660,7 +660,7 @@ static void add_module_pb_graph_pin_interc( module_manager.set_child_instance_name( pb_module, mux_mem_module, mux_mem_instance, mux_mem_instance_name); /* Add this MUX as a configurable child to the pb_module */ - size_t config_child_id = module_manager.num_configurable_child(pb_module, ModuleManager::e_config_child_type::LOGICAL); + size_t config_child_id = module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::LOGICAL); module_manager.add_configurable_child(pb_module, mux_mem_module, mux_mem_instance, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); if (group_config_block) { @@ -671,6 +671,7 @@ static void add_module_pb_graph_pin_interc( VTR_ASSERT(true == module_manager.valid_module_id(phy_mem_module)); module_manager.set_logical2physical_configurable_child(pb_module, config_child_id, phy_mem_module); + VTR_LOGV(verbose, "Now use a feedthrough memory for '%s'\n", phy_mem_module_name.c_str()); } /* Add nets to connect SRAM ports of the MUX to the SRAM port of memory @@ -1076,7 +1077,7 @@ static void rec_build_logical_tile_modules( */ size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, pb_module, circuit_lib, sram_model, mem_module_type); + module_manager, pb_module, circuit_lib, sram_model, mem_module_type, ModuleManager::e_config_child_type::LOGICAL); if (0 < module_num_config_bits) { add_sram_ports_to_module_manager(module_manager, pb_module, circuit_lib, sram_model, mem_module_type, diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index f2d8d4bd3..155fd1ef7 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -342,7 +342,7 @@ static void add_module_nets_to_cmos_memory_scan_chain_module( net_src_module_id = module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[mem_index - 1]; + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -1225,7 +1225,7 @@ int build_memory_modules(ModuleManager& module_manager, *-th pin of output port of the memory module, where W is the size of port * 3. It assumes fixed port name for output ports ********************************************************************/ -void add_module_output_nets_to_memory_group_module( +static void add_module_output_nets_to_memory_group_module( ModuleManager& module_manager, const ModuleId& mem_module, const std::string& mem_module_output_name, const ModuleId& child_module, const size_t& output_pin_start_index, const size_t& child_instance) { @@ -1391,7 +1391,7 @@ int add_physical_memory_module(ModuleManager& module_manager, size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH); + module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH, ModuleManager::e_config_child_type::LOGICAL); /* No need to build a memory when there are no configuration bits required */ if (module_num_config_bits == 0) { return CMD_EXEC_SUCCESS; @@ -1399,7 +1399,7 @@ int add_physical_memory_module(ModuleManager& module_manager, std::string phy_mem_module_name = generate_physical_memory_module_name(module_manager.module_name(curr_module), module_num_config_bits); ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); if (!module_manager.valid_module_id(phy_mem_module)) { - status = build_memory_group_module(module_manager, decode_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules); + status = build_memory_group_module(module_manager, decoder_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules, module_num_config_bits); } if (status != CMD_EXEC_SUCCESS) { VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); @@ -1423,7 +1423,7 @@ int add_physical_memory_module(ModuleManager& module_manager, mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); for (size_t ichild = 0; ichild < module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++ichild) { - for (CircuitPortType port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { + for (e_circuit_model_port_type port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { std::string src_port_name = mem2mem_port_map[port_type]; std::string des_port_name = generate_sram_port_name(CONFIG_MEM_FEEDTHROUGH, port_type); @@ -1450,7 +1450,7 @@ int add_physical_memory_module(ModuleManager& module_manager, /* Create a net and add source and sink to it */ ModuleNetId net = create_module_source_pin_net( module_manager, curr_module, phy_mem_module, phy_mem_instance, - src_port_id, src_port.pins()[cur_mem_pin_index]); + src_port_id, src_port.pins()[curr_mem_pin_index]); if (module_manager.valid_module_net_id(curr_module, net)) { return CMD_EXEC_FATAL_ERROR; } @@ -1471,9 +1471,9 @@ int add_physical_memory_module(ModuleManager& module_manager, } /* Sanity check */ std::map required_mem_child_inst_count; - for (ModuleId curr_module : module_manager.child_modules(phy_mem_module)) { - if (logical_mem_child_inst_count[curr_module] != module_manager.num_instance(phy_mem_module, curr_module)) { - VTR_LOG_ERROR("Expect the %lu instances of module '%s' under its parent '%s' while only updated %lu during logical-to-physical configurable child mapping sync-up!\n", module_manager.num_instance(phy_mem_module, curr_module), module_manager.module_name(curr_module).c_str(), module_manager.module_name(phy_mem_module).c_str(), logical_mem_child_inst_count[curr_module]); + for (ModuleId curr_child_module : module_manager.child_modules(phy_mem_module)) { + if (logical_mem_child_inst_count[curr_child_module] != module_manager.num_instance(phy_mem_module, curr_child_module)) { + VTR_LOG_ERROR("Expect the %lu instances of module '%s' under its parent '%s' while only updated %lu during logical-to-physical configurable child mapping sync-up!\n", module_manager.num_instance(phy_mem_module, curr_child_module), module_manager.module_name(curr_child_module).c_str(), module_manager.module_name(phy_mem_module).c_str(), logical_mem_child_inst_count[curr_child_module]); return CMD_EXEC_FATAL_ERROR; } } diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index 116e3c8e6..1ba7f52c3 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -22,7 +22,7 @@ std::vector add_module_output_nets_to_chain_mem_modules( const CircuitPortId& circuit_port, const ModuleId& child_module, const size_t& child_index, const size_t& child_instance); -void build_memory_modules(ModuleManager& module_manager, +int build_memory_modules(ModuleManager& module_manager, DecoderLibrary& arch_decoder_lib, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, @@ -35,7 +35,8 @@ int build_memory_group_module(ModuleManager& module_manager, const e_config_protocol_type& sram_orgz_type, const std::string& module_name, const CircuitModelId& sram_model, - const std::vector& child_modules); + const std::vector& child_modules, + const size_t& num_mems); int add_physical_memory_module(ModuleManager& module_manager, DecoderLibrary& decoder_lib, diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index 8430cb6e8..1abc20a14 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -752,7 +752,7 @@ static void build_connection_block_mux_module( module_manager, cb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ - size_t config_child_id = module_manager.num_configurable_children(cb_module); + size_t config_child_id = module_manager.num_configurable_children(cb_module, ModuleManager::e_config_child_type::LOGICAL); module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); /* For logical memory, define the physical memory here */ if (group_config_block) { @@ -991,7 +991,7 @@ static void build_connection_block_module( /* Build a physical memory block */ if (group_config_block) { - add_physical_memory_module(module_manager, decoder_lib, sb_module, circuit_lib, sram_orgz_type, sram_model); + add_physical_memory_module(module_manager, decoder_lib, cb_module, circuit_lib, sram_orgz_type, sram_model); } /* Add global ports to the pb_module: @@ -1052,6 +1052,7 @@ static void build_flatten_connection_block_modules( const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, const t_rr_type& cb_type, + const bool& group_config_block, const bool& verbose) { /* Build unique X-direction connection block modules */ vtr::Point cb_range = device_rr_gsb.get_gsb_range(); @@ -1069,7 +1070,7 @@ static void build_flatten_connection_block_modules( build_connection_block_module( module_manager, decoder_lib, device_annotation, device_ctx.grid, device_ctx.rr_graph, circuit_lib, sram_orgz_type, sram_model, rr_gsb, - cb_type, verbose); + cb_type, group_config_block, verbose); } } } diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index a8d279ea6..cf6816b3f 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -220,8 +220,9 @@ static void organize_top_module_tile_memory_modules( vtr::Point config_coord(tile_coord.x() * 2, tile_coord.y() * 2); module_manager.add_configurable_child( top_module, grid_module, + grid_instance_ids[tile_coord.x()][tile_coord.y()], ModuleManager::e_config_child_type::UNIFIED, - grid_instance_ids[tile_coord.x()][tile_coord.y()], config_coord); + config_coord); } } @@ -429,7 +430,7 @@ void organize_top_module_memory_modules( const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy) { /* Ensure clean vectors to return */ - VTR_ASSERT(true == module_manager.configurable_children(top_module).empty()); + VTR_ASSERT(true == module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).empty()); /* First, organize the I/O tiles on the border */ /* Special for the I/O tileas on RIGHT and BOTTOM, @@ -1334,16 +1335,16 @@ static void add_top_module_nets_cmos_memory_bank_config_bus( * configurable children */ module_manager.add_configurable_child(top_module, bl_decoder_module, - curr_bl_decoder_instance_id, false); + curr_bl_decoder_instance_id, ModuleManager::e_config_child_type::PHYSICAL); module_manager.add_configurable_child_to_region( top_module, config_region, bl_decoder_module, curr_bl_decoder_instance_id, - module_manager.logical_configurable_children(top_module).size() - 1); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); module_manager.add_configurable_child(top_module, wl_decoder_module, - curr_wl_decoder_instance_id, false); + curr_wl_decoder_instance_id, ModuleManager::e_config_child_type::PHYSICAL); module_manager.add_configurable_child_to_region( top_module, config_region, wl_decoder_module, curr_wl_decoder_instance_id, - module_manager.logical_configurable_children(top_module).size() - 1); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); } } @@ -1764,7 +1765,7 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( ++mem_index) { ModuleId child_module = configurable_children[mem_index]; size_t child_instance = - module_manager.logical_configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[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 = @@ -1799,7 +1800,7 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( ++mem_index) { ModuleId child_module = configurable_children[mem_index]; size_t child_instance = - module_manager.logical_configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; ModulePortId child_en_port = module_manager.find_module_port( child_module, std::string(DECODER_ENABLE_PORT_NAME)); BasicPort child_en_port_info = @@ -1819,11 +1820,11 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( /* Add the decoder as the last configurable children */ module_manager.add_configurable_child(parent_module, decoder_module, - decoder_instance, false); + decoder_instance, ModuleManager::e_config_child_type::PHYSICAL); /* Register the configurable child to configuration region */ module_manager.add_configurable_child_to_region( parent_module, config_region, decoder_module, decoder_instance, - module_manager.logical_configurable_children(parent_module).size() - 1); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); } /********************************************************************* diff --git a/openfpga/src/fabric/build_top_module_memory_bank.cpp b/openfpga/src/fabric/build_top_module_memory_bank.cpp index 0e5823ae2..d0876add8 100644 --- a/openfpga/src/fabric/build_top_module_memory_bank.cpp +++ b/openfpga/src/fabric/build_top_module_memory_bank.cpp @@ -58,7 +58,7 @@ static void add_module_nets_to_ql_memory_bank_shift_register_module( const std::string& chain_head_port_name, const std::string& chain_tail_port_name) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -79,27 +79,27 @@ static void add_module_nets_to_ql_memory_bank_shift_register_module( /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); net_src_module_id = - module_manager.configurable_children(parent_module)[mem_index - 1]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[mem_index - 1]; + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -133,9 +133,9 @@ static void add_module_nets_to_ql_memory_bank_shift_register_module( /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module).back(); + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module).back(); + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL).back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -245,7 +245,7 @@ static ModuleId build_bl_shift_register_chain_module( module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance); + sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); /* Build module nets to wire bl outputs of sram modules to BL outputs of * memory module */ @@ -363,7 +363,7 @@ static ModuleId build_wl_shift_register_chain_module( module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance); + sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); /* Build module nets to wire wl outputs of sram modules to WL outputs of * memory module */ @@ -699,10 +699,10 @@ static void add_top_module_nets_cmos_ql_memory_bank_bl_decoder_config_bus( * configurable children */ module_manager.add_configurable_child(top_module, bl_decoder_module, - curr_bl_decoder_instance_id); + curr_bl_decoder_instance_id, ModuleManager::e_config_child_type::UNIFIED); module_manager.add_configurable_child_to_region( top_module, config_region, bl_decoder_module, curr_bl_decoder_instance_id, - module_manager.configurable_children(top_module).size() - 1); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); } } @@ -968,10 +968,10 @@ static void add_top_module_nets_cmos_ql_memory_bank_wl_decoder_config_bus( * configurable children */ module_manager.add_configurable_child(top_module, wl_decoder_module, - curr_wl_decoder_instance_id); + curr_wl_decoder_instance_id, ModuleManager::e_config_child_type::UNIFIED); module_manager.add_configurable_child_to_region( top_module, config_region, wl_decoder_module, curr_wl_decoder_instance_id, - module_manager.configurable_children(top_module).size() - 1); + module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); } } diff --git a/openfpga/src/fabric/fabric_key_writer.cpp b/openfpga/src/fabric/fabric_key_writer.cpp index b652b5857..f452ec6f6 100644 --- a/openfpga/src/fabric/fabric_key_writer.cpp +++ b/openfpga/src/fabric/fabric_key_writer.cpp @@ -32,7 +32,7 @@ static int add_module_keys_to_fabric_key(const ModuleManager& module_manager, return CMD_EXEC_SUCCESS; } /* Bypass modules which does not have any configurable children */ - if (module_manager.logical_configurable_children(curr_module).empty()) { + if (module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL).empty()) { return CMD_EXEC_SUCCESS; } /* Now create the module and add subkey one by one */ @@ -41,12 +41,12 @@ static int add_module_keys_to_fabric_key(const ModuleManager& module_manager, return CMD_EXEC_FATAL_ERROR; } size_t num_config_child = - module_manager.logical_configurable_children(curr_module).size(); + module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL).size(); for (size_t ichild = 0; ichild < num_config_child; ++ichild) { ModuleId child_module = - module_manager.logical_configurable_children(curr_module)[ichild]; + module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; size_t child_instance = - module_manager.logical_configurable_child_instances(curr_module)[ichild]; + module_manager.configurable_child_instances(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; FabricSubKeyId sub_key = fabric_key.create_module_key(key_module_id); fabric_key.set_sub_key_name(sub_key, @@ -111,7 +111,7 @@ int write_fabric_key_to_xml_file( /* Build a fabric key database by visiting all the configurable children */ FabricKey fabric_key; - size_t num_keys = module_manager.logical_configurable_children(top_module).size(); + size_t num_keys = module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); fabric_key.reserve_keys(num_keys); diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 72cd67109..e91030e9f 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -412,13 +412,13 @@ size_t ModuleManager::instance_id(const ModuleId& parent_module, return size_t(-1); } -size_t ModuleManager::num_configurable_children(const ModuleId& parent_module, const e_config_child_type& type) const; +size_t ModuleManager::num_configurable_children(const ModuleId& parent_module, const e_config_child_type& type) const { VTR_ASSERT(valid_module_id(parent_module)); if (type == ModuleManager::e_config_child_type::LOGICAL) { return logical_configurable_children_[parent_module].size(); } VTR_ASSERT(type == ModuleManager::e_config_child_type::LOGICAL); - return physical_configurable_children_[parent_module].size() + return physical_configurable_children_[parent_module].size(); } ModuleManager::e_module_port_type ModuleManager::port_type( @@ -716,12 +716,14 @@ ModuleId ModuleManager::add_module(const std::string& name) { child_instance_names_.emplace_back(); logical_configurable_children_.emplace_back(); logical_configurable_child_instances_.emplace_back(); - logical_configurable_child_regions_.emplace_back(); - logical_configurable_child_coordinates_.emplace_back(); - physical_configurable_children_.emplace_back(); physical_configurable_child_instances_.emplace_back(); - physical_configurable_child_parents_.emplace_back(); + physical_configurable_child_regions_.emplace_back(); + physical_configurable_child_coordinates_.emplace_back(); + + logical2physical_configurable_children_.emplace_back(); + logical2physical_configurable_child_instances_.emplace_back(); + logical2physical_configurable_child_parents_.emplace_back(); config_region_ids_.emplace_back(); config_region_children_.emplace_back(); @@ -1001,7 +1003,7 @@ void ModuleManager::add_configurable_child(const ModuleId& parent_module, void ModuleManager::set_logical2physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); + VTR_ASSERT(logical_child_id < num_configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ logical2physical_configurable_children_[parent_module][logical_child_id] = physical_child_module; } @@ -1009,7 +1011,7 @@ void ModuleManager::set_logical2physical_configurable_child(const ModuleId& pare void ModuleManager::set_logical2physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); + VTR_ASSERT(logical_child_id < num_configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ logical2physical_configurable_child_instances_[parent_module][logical_child_id] = physical_child_instance; } @@ -1017,7 +1019,7 @@ void ModuleManager::set_logical2physical_configurable_child_instance(const Modul void ModuleManager::set_logical2physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(logical_child_id < num_logical_configurable_children(parent_module)); + VTR_ASSERT(logical_child_id < num_configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ logical2physical_configurable_child_parents_[parent_module][logical_child_id] = physical_child_parent_module; } @@ -1086,9 +1088,9 @@ void ModuleManager::add_configurable_child_to_region( /* Ensure that the child module is in the configurable children list */ VTR_ASSERT(child_module == - physical_configurable_children(parent_module)[config_child_id]); + configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[config_child_id]); VTR_ASSERT(child_instance == - physical_configurable_child_instances(parent_module)[config_child_id]); + configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[config_child_id]); /* If the child is already in another region, error out */ if ((true == @@ -1426,12 +1428,14 @@ void ModuleManager::clear_configurable_children(const ModuleId& parent_module) { logical_configurable_children_[parent_module].clear(); logical_configurable_child_instances_[parent_module].clear(); - logical_configurable_child_regions_[parent_module].clear(); - logical_configurable_child_coordinates_[parent_module].clear(); - physical_configurable_children_[parent_module].clear(); physical_configurable_child_instances_[parent_module].clear(); - physical_configurable_child_parents_[parent_module].clear(); + physical_configurable_child_regions_[parent_module].clear(); + physical_configurable_child_coordinates_[parent_module].clear(); + + logical2physical_configurable_children_[parent_module].clear(); + logical2physical_configurable_child_instances_[parent_module].clear(); + logical2physical_configurable_child_parents_[parent_module].clear(); } void ModuleManager::clear_config_region(const ModuleId& parent_module) { diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 9e3d721f4..82f2b9dcd 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -193,7 +193,7 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, /* 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), ModuleManager::e_config_child_type::PHYSICAL); + openfpga_ctx.module_graph(), top_module, ModuleManager::e_config_child_type::PHYSICAL)); /* Create bitstream from grids */ VTR_LOGV(verbose, "Building grid bitstream...\n"); diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index c1a4770f1..fa3f34613 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -778,7 +778,7 @@ static void build_physical_block_bitstream( if (!module_manager.unified_configurable_children(grid_module)) { VTR_ASSERT(1 == module_manager.configurable_children(grid_module, ModuleManager::e_config_child_type::PHYSICAL).size()); std::string phy_mem_instance_name = module_manager.instance_name(grid_module, module_manager.configurable_children(grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); - ConfigBlockId grid_grouped_config_block = bitstream_manager.add_child_block(phy_mem_instance_name); + ConfigBlockId grid_grouped_config_block = bitstream_manager.add_block(phy_mem_instance_name); bitstream_manager.add_child_block(grid_configurable_block, grid_grouped_config_block); grid_configurable_block = grid_grouped_config_block; } diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index 006bb0998..32a07fe05 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -534,7 +534,7 @@ static void build_connection_block_bitstreams( if (!module_manager.unified_configurable_children(cb_module)) { VTR_ASSERT(1 == module_manager.configurable_children(cb_module, ModuleManager::e_config_child_type::PHYSICAL).size()); std::string phy_mem_instance_name = module_manager.instance_name(cb_module, module_manager.configurable_children(cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); - ConfigBlockId cb_grouped_config_block = bitstream_manager.add_child_block(phy_mem_instance_name); + ConfigBlockId cb_grouped_config_block = bitstream_manager.add_block(phy_mem_instance_name); bitstream_manager.add_child_block(cb_configurable_block, cb_grouped_config_block); cb_configurable_block = cb_grouped_config_block; } @@ -645,7 +645,7 @@ void build_routing_bitstream( if (!module_manager.unified_configurable_children(sb_module)) { VTR_ASSERT(1 == module_manager.configurable_children(sb_module, ModuleManager::e_config_child_type::PHYSICAL).size()); std::string phy_mem_instance_name = module_manager.instance_name(sb_module, module_manager.configurable_children(sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); - ConfigBlockId sb_grouped_config_block = bitstream_manager.add_child_block(phy_mem_instance_name); + ConfigBlockId sb_grouped_config_block = bitstream_manager.add_block(phy_mem_instance_name); bitstream_manager.add_child_block(sb_configurable_block, sb_grouped_config_block); sb_configurable_block = sb_grouped_config_block; } diff --git a/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp index 1eadf6fda..480e4c915 100644 --- a/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp @@ -49,13 +49,13 @@ static void rec_print_pnr_sdc_constrain_configurable_chain( ModuleId& previous_module) { /* For each configurable child, we will go one level down in priority */ for (size_t child_index = 0; - child_index < module_manager.configurable_children(parent_module).size(); + child_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++child_index) { std::string child_module_path = parent_module_path; ModuleId child_module_id = - module_manager.configurable_children(parent_module)[child_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; size_t child_instance_id = - module_manager.configurable_child_instances(parent_module)[child_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; std::string child_instance_name; if (true == module_manager @@ -79,7 +79,7 @@ static void rec_print_pnr_sdc_constrain_configurable_chain( /* If there is no configurable children any more, this is a leaf module, print * a SDC command for disable timing */ - if (0 < module_manager.configurable_children(parent_module).size()) { + if (0 < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size()) { return; } diff --git a/openfpga/src/fpga_sdc/sdc_memory_utils.cpp b/openfpga/src/fpga_sdc/sdc_memory_utils.cpp index 12d45b887..ef635bd37 100644 --- a/openfpga/src/fpga_sdc/sdc_memory_utils.cpp +++ b/openfpga/src/fpga_sdc/sdc_memory_utils.cpp @@ -45,13 +45,13 @@ void rec_print_pnr_sdc_disable_configurable_memory_module_output( /* For each configurable child, we will go one level down in priority */ for (size_t child_index = 0; - child_index < module_manager.configurable_children(parent_module).size(); + child_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++child_index) { std::string child_module_path = parent_module_path; ModuleId child_module_id = - module_manager.configurable_children(parent_module)[child_index]; + module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; size_t child_instance_id = - module_manager.configurable_child_instances(parent_module)[child_index]; + module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; std::string child_instance_name; if (true == module_manager @@ -96,7 +96,7 @@ void rec_print_pnr_sdc_disable_configurable_memory_module_output( /* If there is no configurable children any more, this is a leaf module, print * a SDC command for disable timing */ - if (0 < module_manager.configurable_children(parent_module).size()) { + if (0 < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size()) { return; } diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index e6c6a0952..a759b1d9d 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -2,9 +2,8 @@ * This file includes functions that are used for * generating ports for memory modules *********************************************************************/ -/* Headers from vtrutil library */ #include "memory_utils.h" - +#include "command_exit_codes.h" #include "decoder_library_utils.h" #include "openfpga_naming.h" #include "vtr_assert.h" @@ -520,16 +519,16 @@ int rec_update_logical_memory_children_with_physical_mapping(ModuleManager& modu ModuleId logical_child = module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; if (module_manager.configurable_children(logical_child, ModuleManager::e_config_child_type::LOGICAL).empty()) { /* This is a leaf node, update its physical information */ - ModuleId phy_mem_submodule = module_manager.logical2physical_configurable_children(curr_module)[ichild] + ModuleId phy_mem_submodule = module_manager.logical2physical_configurable_children(curr_module)[ichild]; auto result = logical_mem_child_inst_count.find(phy_mem_submodule); if (result == logical_mem_child_inst_count.end()) { - logical_mem_child_inst_count.find[phy_mem_submodule] = 0; + logical_mem_child_inst_count[phy_mem_submodule] = 0; } module_manager.set_logical2physical_configurable_child_instance(curr_module, ichild, logical_mem_child_inst_count[phy_mem_submodule]); module_manager.set_logical2physical_configurable_child_parent_module(curr_module, ichild, phy_mem_module); logical_mem_child_inst_count[phy_mem_submodule]++; } else { - rec_find_physical_memory_children(module_manager, logical_child, physical_memory_children, logical_mem_child_inst_count); + rec_update_logical_memory_children_with_physical_mapping(module_manager, logical_child, phy_mem_module, logical_mem_child_inst_count); } } return CMD_EXEC_SUCCESS; diff --git a/openfpga/src/utils/module_manager_memory_utils.cpp b/openfpga/src/utils/module_manager_memory_utils.cpp index 5e6ca8701..6cfcf2dbb 100644 --- a/openfpga/src/utils/module_manager_memory_utils.cpp +++ b/openfpga/src/utils/module_manager_memory_utils.cpp @@ -83,7 +83,7 @@ static bool update_submodule_memory_modules_from_fabric_key( const ModuleManager::e_config_child_type& config_child_type, const FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { /* Reset the configurable children */ - module_manager.clear_configurable_children(module_id, config_child_type); + module_manager.clear_configurable_children(module_id); for (FabricSubKeyId key_id : fabric_key.sub_keys(key_module_id)) { std::pair inst_info(ModuleId::INVALID(), 0); diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 87baecf78..8360aea3b 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -1107,7 +1107,8 @@ void add_module_nets_cmos_flatten_memory_config_bus( void add_module_nets_cmos_memory_bank_bl_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_port_type& config_port_type) { + const e_circuit_model_port_type& config_port_type, + const ModuleManager::e_config_child_type& config_child_type) { /* A counter for the current pin id for the source port of parent module */ size_t cur_src_pin_id = 0; @@ -1124,15 +1125,15 @@ void add_module_nets_cmos_memory_bank_bl_config_bus( module_manager.module_port(net_src_module_id, net_src_port_id); for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); ++mem_index) { /* Find the port name of next memory module */ std::string sink_port_name = generate_sram_port_name(sram_orgz_type, config_port_type); ModuleId net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index]; size_t net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; ModulePortId net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); @@ -1190,7 +1191,8 @@ void add_module_nets_cmos_memory_bank_bl_config_bus( void add_module_nets_cmos_memory_bank_wl_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_port_type& config_port_type) { + const e_circuit_model_port_type& config_port_type, + const ModuleManager::e_config_child_type& config_child_type) { /* A counter for the current pin id for the source port of parent module */ size_t cur_src_pin_id = 0; @@ -1219,15 +1221,15 @@ void add_module_nets_cmos_memory_bank_wl_config_bus( module_manager.module_port(net_src_module_id, net_bl_port_id); for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); + mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); ++mem_index) { /* Find the port name of next memory module */ std::string sink_port_name = generate_sram_port_name(sram_orgz_type, config_port_type); ModuleId net_sink_module_id = - module_manager.configurable_children(parent_module)[mem_index]; + module_manager.configurable_children(parent_module, config_child_type)[mem_index]; size_t net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; + module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; ModulePortId net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); diff --git a/openfpga/src/utils/module_manager_utils.h b/openfpga/src/utils/module_manager_utils.h index b1e02d79f..ca3a1ac30 100644 --- a/openfpga/src/utils/module_manager_utils.h +++ b/openfpga/src/utils/module_manager_utils.h @@ -116,12 +116,14 @@ void add_module_nets_cmos_flatten_memory_config_bus( void add_module_nets_cmos_memory_bank_bl_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_port_type& config_port_type); + const e_circuit_model_port_type& config_port_type, + const ModuleManager::e_config_child_type& config_child_type); void add_module_nets_cmos_memory_bank_wl_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, const e_config_protocol_type& sram_orgz_type, - const e_circuit_model_port_type& config_port_type); + const e_circuit_model_port_type& config_port_type, + const ModuleManager::e_config_child_type& config_child_type); void add_module_nets_cmos_memory_chain_config_bus( ModuleManager& module_manager, const ModuleId& parent_module, @@ -151,8 +153,7 @@ size_t find_module_num_shared_config_bits(const ModuleManager& module_manager, size_t find_module_num_config_bits( const ModuleManager& module_manager, const ModuleId& module_id, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, - const e_config_protocol_type& sram_orgz_type, - const ModuleManager::e_config_child_type& config_child_type); + const e_config_protocol_type& sram_orgz_type); void add_module_global_input_ports_from_child_modules( ModuleManager& module_manager, const ModuleId& module_id, @@ -176,7 +177,8 @@ size_t find_module_num_shared_config_bits_from_child_modules( size_t find_module_num_config_bits_from_child_modules( ModuleManager& module_manager, const ModuleId& module_id, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, - const e_config_protocol_type& sram_orgz_type); + const e_config_protocol_type& sram_orgz_type, + const ModuleManager::e_config_child_type& config_child_type); ModuleNetId create_module_source_pin_net(ModuleManager& module_manager, const ModuleId& cur_module_id, From d3895c3dc0ee344e8bc73032b3abd3539dc2fe24 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 17:34:25 -0700 Subject: [PATCH 14/41] [core] code format --- libs/libarchopenfpga/src/circuit_types.h | 6 +- .../src/openfpga_reserved_words.h | 6 +- openfpga/src/base/openfpga_naming.cpp | 26 +- openfpga/src/base/openfpga_naming.h | 26 +- openfpga/src/fabric/build_device_module.cpp | 25 +- .../fabric/build_fpga_core_wrapper_module.cpp | 3 +- openfpga/src/fabric/build_grid_modules.cpp | 130 ++++---- openfpga/src/fabric/build_memory_modules.cpp | 293 +++++++++++------- openfpga/src/fabric/build_memory_modules.h | 10 +- openfpga/src/fabric/build_routing_modules.cpp | 86 +++-- openfpga/src/fabric/build_routing_modules.h | 6 +- openfpga/src/fabric/build_tile_modules.cpp | 24 +- openfpga/src/fabric/build_top_module.cpp | 5 +- .../build_top_module_child_tile_instance.cpp | 6 +- .../src/fabric/build_top_module_memory.cpp | 102 ++++-- .../fabric/build_top_module_memory_bank.cpp | 72 +++-- openfpga/src/fabric/fabric_key_writer.cpp | 24 +- openfpga/src/fabric/module_manager.cpp | 158 ++++++---- openfpga/src/fabric/module_manager.h | 97 +++--- .../fpga_bitstream/build_device_bitstream.cpp | 27 +- .../fpga_bitstream/build_fabric_bitstream.cpp | 43 ++- .../build_fabric_bitstream_memory_bank.cpp | 8 +- .../fpga_bitstream/build_grid_bitstream.cpp | 39 ++- .../build_routing_bitstream.cpp | 58 +++- .../configuration_chain_sdc_writer.cpp | 19 +- openfpga/src/fpga_sdc/sdc_memory_utils.cpp | 19 +- openfpga/src/utils/memory_utils.cpp | 78 +++-- openfpga/src/utils/memory_utils.h | 22 +- .../src/utils/module_manager_memory_utils.cpp | 70 +++-- openfpga/src/utils/module_manager_utils.cpp | 152 +++++---- openfpga/src/utils/module_manager_utils.h | 3 +- 31 files changed, 1052 insertions(+), 591 deletions(-) diff --git a/libs/libarchopenfpga/src/circuit_types.h b/libs/libarchopenfpga/src/circuit_types.h index 58107a0cc..86143bb65 100644 --- a/libs/libarchopenfpga/src/circuit_types.h +++ b/libs/libarchopenfpga/src/circuit_types.h @@ -134,7 +134,8 @@ constexpr std::array * - configurable memories are organized and accessed by quicklogic memory bank * - configurable memories are organized and accessed by memory bank * - configurable memories are organized and accessed by frames - * - configurable memories are organized and accessed by feedthrough. Currently, this is only for internal use only + * - configurable memories are organized and accessed by feedthrough. Currently, + * this is only for internal use only */ enum e_config_protocol_type { CONFIG_MEM_STANDALONE, @@ -148,6 +149,7 @@ enum e_config_protocol_type { constexpr std::array CONFIG_PROTOCOL_TYPE_STRING = {{"standalone", "scan_chain", "memory_bank", - "ql_memory_bank", "frame_based", "feedthrough"}}; + "ql_memory_bank", "frame_based", + "feedthrough"}}; #endif diff --git a/libs/libopenfpgautil/src/openfpga_reserved_words.h b/libs/libopenfpgautil/src/openfpga_reserved_words.h index cd6b6148f..3317b12ff 100644 --- a/libs/libopenfpgautil/src/openfpga_reserved_words.h +++ b/libs/libopenfpgautil/src/openfpga_reserved_words.h @@ -42,8 +42,10 @@ constexpr const char* SWITCH_BLOCK_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_"; constexpr const char* MEMORY_MODULE_POSTFIX = "_mem"; constexpr const char* MEMORY_FEEDTHROUGH_MODULE_POSTFIX = "_feedthrough_mem"; -constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME = "feedthrough_mem_in"; -constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME = "feedthrough_mem_inb"; +constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME = + "feedthrough_mem_in"; +constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME = + "feedthrough_mem_inb"; constexpr const char* MEMORY_BL_PORT_NAME = "bl"; constexpr const char* MEMORY_WL_PORT_NAME = "wl"; constexpr const char* MEMORY_WLR_PORT_NAME = "wlr"; diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 9a8e64c5e..1623df96f 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -234,7 +234,7 @@ std::string generate_memory_module_name(const CircuitLibrary& circuit_lib, const bool& feedthrough_memory) { std::string mid_name; if (feedthrough_memory) { - mid_name = "feedthrough_"; + mid_name = "feedthrough_"; } return std::string(circuit_lib.model_name(circuit_model) + "_" + mid_name + circuit_lib.model_name(sram_model) + postfix); @@ -531,8 +531,10 @@ std::string generate_tile_module_netlist_name(const std::string& block_name, /********************************************************************* * Generate the module name of a physical memory module **********************************************************************/ -std::string generate_physical_memory_module_name(const std::string& prefix, const size_t& mem_size) { - return prefix + std::string("_config_group_mem_size") + std::to_string(mem_size); +std::string generate_physical_memory_module_name(const std::string& prefix, + const size_t& mem_size) { + return prefix + std::string("_config_group_mem_size") + + std::to_string(mem_size); } /********************************************************************* @@ -748,17 +750,17 @@ std::string generate_sram_port_name( switch (sram_orgz_type) { case CONFIG_MEM_FEEDTHROUGH: - /* Two types of ports are available: + /* Two types of ports are available: * (1) BL indicates the mem port * (2) BLB indicates the inverted mem port * - * mem mem_inv - * [0] [0] - * | | - * v v - * +----------------+ - * | Virtual Mem | - * +----------------+ + * mem mem_inv + * [0] [0] + * | | + * v v + * +----------------+ + * | Virtual Mem | + * +----------------+ */ if (CIRCUIT_MODEL_PORT_BL == port_type) { port_name = std::string(MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME); @@ -1202,7 +1204,7 @@ std::string generate_pb_memory_instance_name(const std::string& prefix, t_pb_graph_pin* pb_graph_pin, const std::string& postfix, const bool& feedthrough_memory) { - std::string mid_name = feedthrough_memory ? "virtual_" : ""; + std::string mid_name = feedthrough_memory ? "virtual_" : ""; std::string instance_name(mid_name + prefix); instance_name += std::string(pb_graph_pin->parent_node->pb_type->name); diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index c6a84b3e3..4a1d35e8e 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -119,38 +119,34 @@ std::string generate_tile_module_port_name(const std::string& prefix, std::string generate_tile_module_netlist_name(const std::string& block_name, const std::string& postfix); -std::string generate_physical_memory_module_name(const std::string& prefix, const size_t& mem_size); +std::string generate_physical_memory_module_name(const std::string& prefix, + const size_t& mem_size); std::string generate_sb_mux_instance_name(const std::string& prefix, const e_side& sb_side, const size_t& track_id, const std::string& postfix); -std::string generate_sb_memory_instance_name(const std::string& prefix, - const e_side& sb_side, - const size_t& track_id, - const std::string& postfix, - const bool& feedthrough_memory = false); +std::string generate_sb_memory_instance_name( + const std::string& prefix, const e_side& sb_side, const size_t& track_id, + const std::string& postfix, const bool& feedthrough_memory = false); std::string generate_cb_mux_instance_name(const std::string& prefix, const e_side& cb_side, const size_t& pin_id, const std::string& postfix); -std::string generate_cb_memory_instance_name(const std::string& prefix, - const e_side& cb_side, - const size_t& pin_id, - const std::string& postfix, - const bool& feedthrough_memory = false); +std::string generate_cb_memory_instance_name( + const std::string& prefix, const e_side& cb_side, const size_t& pin_id, + const std::string& postfix, const bool& feedthrough_memory = false); std::string generate_pb_mux_instance_name(const std::string& prefix, t_pb_graph_pin* pb_graph_pin, const std::string& postfix); -std::string generate_pb_memory_instance_name(const std::string& prefix, - t_pb_graph_pin* pb_graph_pin, - const std::string& postfix, - const bool& feedthrough_memory = false); +std::string generate_pb_memory_instance_name( + const std::string& prefix, t_pb_graph_pin* pb_graph_pin, + const std::string& postfix, const bool& feedthrough_memory = false); std::string generate_grid_port_name(const size_t& width, const size_t& height, const int& subtile_index, diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index ebf9a7a05..5a833c88a 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -78,7 +78,8 @@ int build_device_module_graph( /* Build memory modules */ build_memory_modules(module_manager, decoder_lib, openfpga_ctx.mux_lib(), openfpga_ctx.arch().circuit_lib, - openfpga_ctx.arch().config_protocol.type(), group_config_block); + openfpga_ctx.arch().config_protocol.type(), + group_config_block); /* Build grid and programmable block modules */ build_grid_modules(module_manager, decoder_lib, vpr_device_ctx, @@ -88,18 +89,20 @@ int build_device_module_graph( duplicate_grid_pin, group_config_block, verbose); if (true == compress_routing) { - build_unique_routing_modules( - module_manager, decoder_lib, vpr_device_ctx, - openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), - openfpga_ctx.arch().circuit_lib, - openfpga_ctx.arch().config_protocol.type(), sram_model, group_config_block, verbose); + build_unique_routing_modules(module_manager, decoder_lib, vpr_device_ctx, + openfpga_ctx.vpr_device_annotation(), + openfpga_ctx.device_rr_gsb(), + openfpga_ctx.arch().circuit_lib, + openfpga_ctx.arch().config_protocol.type(), + sram_model, group_config_block, verbose); } else { VTR_ASSERT_SAFE(false == compress_routing); - build_flatten_routing_modules( - module_manager, decoder_lib, vpr_device_ctx, - openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), - openfpga_ctx.arch().circuit_lib, - openfpga_ctx.arch().config_protocol.type(), sram_model, group_config_block, verbose); + build_flatten_routing_modules(module_manager, decoder_lib, vpr_device_ctx, + openfpga_ctx.vpr_device_annotation(), + openfpga_ctx.device_rr_gsb(), + openfpga_ctx.arch().circuit_lib, + openfpga_ctx.arch().config_protocol.type(), + sram_model, group_config_block, verbose); } /* Build tile modules if defined */ diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index d79260583..d8af89dc5 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -381,7 +381,8 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, /* Now fpga_core should be the only configurable child under the top-level * module */ - module_manager.add_configurable_child(new_top_module, top_module, 0, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + new_top_module, top_module, 0, ModuleManager::e_config_child_type::UNIFIED); return status; } diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 358975fd5..97ce3e8bd 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -311,7 +311,8 @@ static void build_primitive_block_module( } /* Regular (independent) SRAM ports */ - e_config_protocol_type mem_module_type = group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; + e_config_protocol_type mem_module_type = + group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; size_t num_config_bits = find_circuit_num_config_bits(mem_module_type, circuit_lib, primitive_model); if (0 < num_config_bits) { @@ -335,9 +336,9 @@ static void build_primitive_block_module( circuit_lib, primitive_pb_graph_node->pb_type, device_annotation); /* Add the associated memory module as a child of primitive module */ - std::string memory_module_name = - generate_memory_module_name(circuit_lib, primitive_model, sram_model, - std::string(MEMORY_MODULE_POSTFIX), group_config_block); + std::string memory_module_name = generate_memory_module_name( + circuit_lib, primitive_model, sram_model, + std::string(MEMORY_MODULE_POSTFIX), group_config_block); ModuleId memory_module = module_manager.find_module(memory_module_name); /* If there is no memory module required, we can skip the assocated net @@ -357,17 +358,21 @@ static void build_primitive_block_module( module_manager, primitive_module, logic_module, logic_instance_id, memory_module, memory_instance_id, circuit_lib, primitive_model); /* Record memory-related information */ - size_t config_child_id = module_manager.num_configurable_children(primitive_module, ModuleManager::e_config_child_type::LOGICAL); - module_manager.add_configurable_child(primitive_module, memory_module, - memory_instance_id, - group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); + size_t config_child_id = module_manager.num_configurable_children( + primitive_module, ModuleManager::e_config_child_type::LOGICAL); + module_manager.add_configurable_child( + primitive_module, memory_module, memory_instance_id, + group_config_block ? ModuleManager::e_config_child_type::LOGICAL + : ModuleManager::e_config_child_type::UNIFIED); /* For logical memory, define the physical memory here */ if (group_config_block) { std::string physical_memory_module_name = generate_memory_module_name(circuit_lib, primitive_model, sram_model, std::string(MEMORY_MODULE_POSTFIX), false); - ModuleId physical_memory_module = module_manager.find_module(physical_memory_module_name); - module_manager.set_logical2physical_configurable_child(primitive_module, config_child_id, physical_memory_module); + ModuleId physical_memory_module = + module_manager.find_module(physical_memory_module_name); + module_manager.set_logical2physical_configurable_child( + primitive_module, config_child_id, physical_memory_module); } } @@ -375,11 +380,12 @@ static void build_primitive_block_module( * primitive modules This is a one-shot addition that covers all the memory * modules in this primitive module! */ - if (0 < module_manager.num_configurable_children(primitive_module, ModuleManager::e_config_child_type::LOGICAL)) { - add_module_nets_memory_config_bus(module_manager, decoder_lib, - primitive_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model), - ModuleManager::e_config_child_type::LOGICAL); + if (0 < module_manager.num_configurable_children( + primitive_module, ModuleManager::e_config_child_type::LOGICAL)) { + add_module_nets_memory_config_bus( + module_manager, decoder_lib, primitive_module, sram_orgz_type, + circuit_lib.design_tech_type(sram_model), + ModuleManager::e_config_child_type::LOGICAL); } /* Add global ports to the pb_module: @@ -642,9 +648,9 @@ static void add_module_pb_graph_pin_interc( generate_mux_subckt_name(circuit_lib, interc_circuit_model, fan_in, std::string(MEMORY_MODULE_POSTFIX)); if (group_config_block) { - mux_mem_module_name = - generate_mux_subckt_name(circuit_lib, interc_circuit_model, fan_in, - std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + mux_mem_module_name = generate_mux_subckt_name( + circuit_lib, interc_circuit_model, fan_in, + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); } ModuleId mux_mem_module = module_manager.find_module(mux_mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(mux_mem_module)); @@ -656,22 +662,28 @@ static void add_module_pb_graph_pin_interc( * generation to modules */ std::string mux_mem_instance_name = generate_pb_memory_instance_name( - GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), group_config_block); + GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), + group_config_block); module_manager.set_child_instance_name( pb_module, mux_mem_module, mux_mem_instance, mux_mem_instance_name); /* Add this MUX as a configurable child to the pb_module */ - size_t config_child_id = module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::LOGICAL); - module_manager.add_configurable_child(pb_module, mux_mem_module, - mux_mem_instance, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); + size_t config_child_id = module_manager.num_configurable_children( + pb_module, ModuleManager::e_config_child_type::LOGICAL); + module_manager.add_configurable_child( + pb_module, mux_mem_module, mux_mem_instance, + group_config_block ? ModuleManager::e_config_child_type::LOGICAL + : ModuleManager::e_config_child_type::UNIFIED); if (group_config_block) { std::string phy_mem_module_name = generate_mux_subckt_name(circuit_lib, interc_circuit_model, fan_in, std::string(MEMORY_MODULE_POSTFIX)); - ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); + ModuleId phy_mem_module = + module_manager.find_module(phy_mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(phy_mem_module)); - module_manager.set_logical2physical_configurable_child(pb_module, config_child_id, - phy_mem_module); - VTR_LOGV(verbose, "Now use a feedthrough memory for '%s'\n", phy_mem_module_name.c_str()); + module_manager.set_logical2physical_configurable_child( + pb_module, config_child_id, phy_mem_module); + VTR_LOGV(verbose, "Now use a feedthrough memory for '%s'\n", + phy_mem_module_name.c_str()); } /* Add nets to connect SRAM ports of the MUX to the SRAM port of memory @@ -984,7 +996,8 @@ static void rec_build_logical_tile_modules( std::vector memory_modules; std::vector memory_instances; - e_config_protocol_type mem_module_type = group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; + e_config_protocol_type mem_module_type = + group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type; /* Add all the child Verilog modules as instances */ for (int ichild = 0; ichild < physical_mode->num_pb_type_children; ++ichild) { @@ -1029,8 +1042,10 @@ static void rec_build_logical_tile_modules( if (0 < find_module_num_config_bits(module_manager, child_pb_module, circuit_lib, sram_model, mem_module_type)) { - module_manager.add_configurable_child(pb_module, child_pb_module, - child_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + pb_module, child_pb_module, child_instance_id, + group_config_block ? ModuleManager::e_config_child_type::LOGICAL + : ModuleManager::e_config_child_type::UNIFIED); } } } @@ -1077,7 +1092,8 @@ static void rec_build_logical_tile_modules( */ size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, pb_module, circuit_lib, sram_model, mem_module_type, ModuleManager::e_config_child_type::LOGICAL); + module_manager, pb_module, circuit_lib, sram_model, mem_module_type, + ModuleManager::e_config_child_type::LOGICAL); if (0 < module_num_config_bits) { add_sram_ports_to_module_manager(module_manager, pb_module, circuit_lib, sram_model, mem_module_type, @@ -1088,11 +1104,12 @@ static void rec_build_logical_tile_modules( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::LOGICAL)) { - add_module_nets_memory_config_bus(module_manager, decoder_lib, pb_module, - mem_module_type, - circuit_lib.design_tech_type(sram_model), - ModuleManager::e_config_child_type::LOGICAL); + if (0 < module_manager.num_configurable_children( + pb_module, ModuleManager::e_config_child_type::LOGICAL)) { + add_module_nets_memory_config_bus( + module_manager, decoder_lib, pb_module, mem_module_type, + circuit_lib.design_tech_type(sram_model), + ModuleManager::e_config_child_type::LOGICAL); } VTR_LOGV(verbose, "Done\n"); @@ -1113,8 +1130,7 @@ static void build_physical_tile_module( const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, t_physical_tile_type_ptr phy_block_type, const e_side& border_side, const bool& duplicate_grid_pin, - const bool& group_config_block, - const bool& verbose) { + const bool& group_config_block, const bool& verbose) { /* Create a Module for the top-level physical block, and add to module manager */ std::string grid_module_name = generate_grid_block_module_name( @@ -1164,19 +1180,23 @@ static void build_physical_tile_module( /* Identify if this sub module includes configuration bits, * we will update the memory module and instance list */ - if (0 < find_module_num_config_bits(module_manager, pb_module, - circuit_lib, sram_model, - group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type)) { - /* Only add logical configurable children here. Since we will add a physical memory block at this level */ - module_manager.add_configurable_child(grid_module, pb_module, - pb_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); + if (0 < find_module_num_config_bits( + module_manager, pb_module, circuit_lib, sram_model, + group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type)) { + /* Only add logical configurable children here. Since we will add a + * physical memory block at this level */ + module_manager.add_configurable_child( + grid_module, pb_module, pb_instance_id, + group_config_block ? ModuleManager::e_config_child_type::LOGICAL + : ModuleManager::e_config_child_type::UNIFIED); } } } /* TODO: Add a physical memory block */ if (group_config_block) { - add_physical_memory_module(module_manager, decoder_lib, grid_module, circuit_lib, sram_orgz_type, sram_model); + add_physical_memory_module(module_manager, decoder_lib, grid_module, + circuit_lib, sram_orgz_type, sram_model); } /* Add grid ports(pins) to the module */ @@ -1264,10 +1284,13 @@ static void build_physical_tile_module( * we just need to find all the I/O ports from the child modules and build a * list of it */ - ModuleManager::e_config_child_type config_child_type = group_config_block ? ModuleManager::e_config_child_type::PHYSICAL : ModuleManager::e_config_child_type::LOGICAL; + ModuleManager::e_config_child_type config_child_type = + group_config_block ? ModuleManager::e_config_child_type::PHYSICAL + : ModuleManager::e_config_child_type::LOGICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, grid_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); + module_manager, grid_module, circuit_lib, sram_model, sram_orgz_type, + config_child_type); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, grid_module, circuit_lib, sram_model, sram_orgz_type, @@ -1278,7 +1301,8 @@ static void build_physical_tile_module( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.num_configurable_children(grid_module, config_child_type)) { + if (0 < module_manager.num_configurable_children(grid_module, + config_child_type)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, grid_module, sram_orgz_type, circuit_lib.design_tech_type(sram_model), config_child_type); @@ -1357,17 +1381,17 @@ void build_grid_modules( std::set io_type_sides = find_physical_io_tile_located_sides(device_ctx.grid, &physical_tile); for (const e_side& io_type_side : io_type_sides) { - build_physical_tile_module(module_manager, decoder_lib, - device_annotation, circuit_lib, - sram_orgz_type, sram_model, &physical_tile, - io_type_side, duplicate_grid_pin, group_config_block, verbose); + build_physical_tile_module( + module_manager, decoder_lib, device_annotation, circuit_lib, + sram_orgz_type, sram_model, &physical_tile, io_type_side, + duplicate_grid_pin, group_config_block, verbose); } } else { /* For CLB and heterogenenous blocks */ build_physical_tile_module(module_manager, decoder_lib, device_annotation, circuit_lib, sram_orgz_type, sram_model, - &physical_tile, NUM_SIDES, duplicate_grid_pin, group_config_block, - verbose); + &physical_tile, NUM_SIDES, duplicate_grid_pin, + group_config_block, verbose); } } VTR_LOG("Done\n"); diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 155fd1ef7..e9d7b9ae3 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -3,19 +3,21 @@ * the memories that are affiliated to multiplexers and other programmable * circuit models, such as IOPADs, LUTs, etc. ********************************************************************/ +#include "build_memory_modules.h" + #include #include #include -#include "command_exit_codes.h" + #include "build_decoder_modules.h" -#include "build_memory_modules.h" #include "circuit_library_utils.h" +#include "command_exit_codes.h" #include "decoder_library_utils.h" +#include "memory_utils.h" #include "module_manager.h" #include "module_manager_utils.h" #include "mux_graph.h" #include "mux_utils.h" -#include "memory_utils.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" #include "vtr_assert.h" @@ -173,7 +175,11 @@ static void add_module_nets_to_cmos_memory_config_chain_module( const CircuitLibrary& circuit_lib, const CircuitPortId& model_input_port, const CircuitPortId& model_output_port) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL).size(); + mem_index < + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::LOGICAL) + .size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -193,28 +199,30 @@ static void add_module_nets_to_cmos_memory_config_chain_module( /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); - net_sink_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); - net_src_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; + net_src_module_id = module_manager.configurable_children( + parent_module, + ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; + parent_module, + ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); - net_sink_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -248,9 +256,15 @@ static void add_module_nets_to_cmos_memory_config_chain_module( /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL).back(); + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::LOGICAL) + .back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL).back(); + module_manager + .configurable_child_instances(parent_module, + ModuleManager::e_config_child_type::LOGICAL) + .back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -310,7 +324,11 @@ static void add_module_nets_to_cmos_memory_scan_chain_module( const CircuitLibrary& circuit_lib, const CircuitPortId& model_input_port, const CircuitPortId& model_output_port) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL).size(); + mem_index < + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::LOGICAL) + .size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -330,28 +348,30 @@ static void add_module_nets_to_cmos_memory_scan_chain_module( /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); - net_sink_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); - net_src_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; + net_src_module_id = module_manager.configurable_children( + parent_module, + ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; + parent_module, + ModuleManager::e_config_child_type::LOGICAL)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); - net_sink_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::LOGICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -479,8 +499,9 @@ static void build_memory_flatten_module(ModuleManager& module_manager, size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); - module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + mem_module, sram_mem_module, sram_mem_instance, + ModuleManager::e_config_child_type::UNIFIED); /* Build module nets */ /* Wire inputs of parent module to inputs of child modules */ @@ -612,8 +633,9 @@ static void build_memory_chain_module(ModuleManager& module_manager, size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); - module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + mem_module, sram_mem_module, sram_mem_instance, + ModuleManager::e_config_child_type::UNIFIED); /* Build module nets to wire outputs of sram modules to outputs of memory * module */ @@ -829,8 +851,9 @@ static void build_frame_memory_module(ModuleManager& module_manager, size_t sram_instance = module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); - module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + mem_module, sram_mem_module, sram_instance, + ModuleManager::e_config_child_type::UNIFIED); /* Wire data_in port to SRAM BL port */ ModulePortId sram_bl_port = module_manager.find_module_port( @@ -890,7 +913,8 @@ static void build_frame_memory_module(ModuleManager& module_manager, add_module_global_ports_from_child_modules(module_manager, mem_module); /* Add the decoder as the last configurable children */ - module_manager.add_configurable_child(mem_module, decoder_module, 0, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + mem_module, decoder_module, 0, ModuleManager::e_config_child_type::UNIFIED); } /********************************************************************* @@ -964,10 +988,12 @@ static int build_feedthrough_memory_module(ModuleManager& module_manager, /* Add module ports */ /* Input: memory inputs */ - BasicPort in_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME), num_mems); + BasicPort in_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME), + num_mems); ModulePortId mem_in_port = module_manager.add_port( mem_module, in_port, ModuleManager::MODULE_INPUT_PORT); - BasicPort inb_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME), num_mems); + BasicPort inb_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME), + num_mems); ModulePortId mem_inb_port = module_manager.add_port( mem_module, inb_port, ModuleManager::MODULE_INPUT_PORT); @@ -975,7 +1001,8 @@ static int build_feedthrough_memory_module(ModuleManager& module_manager, BasicPort out_port(std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME), num_mems); ModulePortId mem_out_port = module_manager.add_port( mem_module, out_port, ModuleManager::MODULE_OUTPUT_PORT); - BasicPort outb_port(std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME), num_mems); + BasicPort outb_port(std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME), + num_mems); ModulePortId mem_outb_port = module_manager.add_port( mem_module, outb_port, ModuleManager::MODULE_OUTPUT_PORT); @@ -987,8 +1014,8 @@ static int build_feedthrough_memory_module(ModuleManager& module_manager, } module_manager.add_module_net_source(mem_module, net, mem_module, 0, mem_in_port, in_port.pins()[pin_id]); - module_manager.add_module_net_sink( - mem_module, net, mem_module, 0, mem_out_port, out_port.pins()[pin_id]); + module_manager.add_module_net_sink(mem_module, net, mem_module, 0, + mem_out_port, out_port.pins()[pin_id]); } for (size_t pin_id = 0; pin_id < inb_port.pins().size(); ++pin_id) { ModuleNetId net = module_manager.create_module_net(mem_module); @@ -997,14 +1024,13 @@ static int build_feedthrough_memory_module(ModuleManager& module_manager, } module_manager.add_module_net_source(mem_module, net, mem_module, 0, mem_inb_port, inb_port.pins()[pin_id]); - module_manager.add_module_net_sink( - mem_module, net, mem_module, 0, mem_outb_port, outb_port.pins()[pin_id]); + module_manager.add_module_net_sink(mem_module, net, mem_module, 0, + mem_outb_port, outb_port.pins()[pin_id]); } return CMD_EXEC_SUCCESS; } - /********************************************************************* * Generate Verilog modules for the memories that are used * by multiplexers @@ -1070,7 +1096,7 @@ static void build_mux_memory_module( * | | | | * v v ... v v * +----------------+ - * | Memory Module | + * | Memory Module | * +----------------+ * | | ... | | * v v v v SRAM ports of multiplexer @@ -1079,8 +1105,7 @@ static void build_mux_memory_module( * +---------------------+ ********************************************************************/ static int build_mux_feedthrough_memory_module( - ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, + ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& mux_model, const MuxGraph& mux_graph) { int status = CMD_EXEC_SUCCESS; @@ -1100,7 +1125,8 @@ static int build_mux_feedthrough_memory_module( mux_graph.num_inputs()), std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); - status = build_feedthrough_memory_module(module_manager, module_name, num_config_bits); + status = build_feedthrough_memory_module(module_manager, module_name, + num_config_bits); break; } case CIRCUIT_MODEL_DESIGN_RRAM: @@ -1118,7 +1144,6 @@ static int build_mux_feedthrough_memory_module( return status; } - /********************************************************************* * Build modules for * the memories that are affiliated to multiplexers and other programmable @@ -1134,14 +1159,15 @@ static int build_mux_feedthrough_memory_module( * memory modules. * Take another example, the memory circuit can implement the scan-chain or * memory-bank organization for the memories. - * If we need feedthrough memory blocks, build the memory modules which contain only feedthrough wires + * If we need feedthrough memory blocks, build the memory modules which contain + *only feedthrough wires ********************************************************************/ int build_memory_modules(ModuleManager& module_manager, - DecoderLibrary& arch_decoder_lib, - const MuxLibrary& mux_lib, - const CircuitLibrary& circuit_lib, - const e_config_protocol_type& sram_orgz_type, - const bool& require_feedthrough_memory) { + DecoderLibrary& arch_decoder_lib, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const bool& require_feedthrough_memory) { int status = CMD_EXEC_SUCCESS; vtr::ScopedStartFinishTimer timer("Build memory modules"); @@ -1161,8 +1187,8 @@ int build_memory_modules(ModuleManager& module_manager, sram_orgz_type, mux_model, mux_graph); /* Create feedthrough memory module */ if (require_feedthrough_memory) { - status = build_mux_feedthrough_memory_module(module_manager, circuit_lib, - sram_orgz_type, mux_model, mux_graph); + status = build_mux_feedthrough_memory_module( + module_manager, circuit_lib, sram_orgz_type, mux_model, mux_graph); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -1205,7 +1231,8 @@ int build_memory_modules(ModuleManager& module_manager, sram_orgz_type, module_name, sram_models[0], num_mems); /* Create feedthrough memory module */ if (require_feedthrough_memory) { - status = build_feedthrough_memory_module(module_manager, module_name, num_mems); + status = + build_feedthrough_memory_module(module_manager, module_name, num_mems); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -1230,8 +1257,8 @@ static void add_module_output_nets_to_memory_group_module( const std::string& mem_module_output_name, const ModuleId& child_module, const size_t& output_pin_start_index, const size_t& child_instance) { /* Wire inputs of parent module to inputs of child modules */ - ModulePortId src_port_id = module_manager.find_module_port( - child_module, mem_module_output_name); + ModulePortId src_port_id = + module_manager.find_module_port(child_module, mem_module_output_name); ModulePortId sink_port_id = module_manager.find_module_port(mem_module, mem_module_output_name); for (size_t pin_id = 0; @@ -1280,7 +1307,8 @@ int build_memory_group_module(ModuleManager& module_manager, module_manager.add_port(mem_module, out_port, ModuleManager::MODULE_OUTPUT_PORT); - std::string outb_port_name = generate_configurable_memory_inverted_data_out_name(); + std::string outb_port_name = + generate_configurable_memory_inverted_data_out_name(); BasicPort outb_port(outb_port_name, num_mems); module_manager.add_port(mem_module, outb_port, ModuleManager::MODULE_OUTPUT_PORT); @@ -1290,25 +1318,33 @@ int build_memory_group_module(ModuleManager& module_manager, size_t mem_outb_pin_start_index = 0; for (size_t ichild = 0; ichild < child_modules.size(); ++ichild) { ModuleId child_module = child_modules[ichild]; - size_t child_instance = module_manager.num_instance(mem_module, child_module); + size_t child_instance = + module_manager.num_instance(mem_module, child_module); module_manager.add_child_module(mem_module, child_module, false); - module_manager.add_configurable_child(mem_module, child_module, child_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + mem_module, child_module, child_instance, + ModuleManager::e_config_child_type::UNIFIED); /* Wire outputs of child module to outputs of parent module */ add_module_output_nets_to_memory_group_module( - module_manager, mem_module, out_port_name, - child_module, mem_out_pin_start_index, child_instance); + module_manager, mem_module, out_port_name, child_module, + mem_out_pin_start_index, child_instance); add_module_output_nets_to_memory_group_module( - module_manager, mem_module, outb_port_name, - child_module, mem_outb_pin_start_index, child_instance); + module_manager, mem_module, outb_port_name, child_module, + mem_outb_pin_start_index, child_instance); /* Update pin counter */ - ModulePortId child_out_port_id = module_manager.find_module_port(child_module, out_port_name); - mem_out_pin_start_index += module_manager.module_port(child_module, child_out_port_id).get_width(); + ModulePortId child_out_port_id = + module_manager.find_module_port(child_module, out_port_name); + mem_out_pin_start_index += + module_manager.module_port(child_module, child_out_port_id).get_width(); - ModulePortId child_outb_port_id = module_manager.find_module_port(child_module, outb_port_name); - mem_outb_pin_start_index += module_manager.module_port(child_module, child_outb_port_id).get_width(); + ModulePortId child_outb_port_id = + module_manager.find_module_port(child_module, outb_port_name); + mem_outb_pin_start_index += + module_manager.module_port(child_module, child_outb_port_id).get_width(); } /* Check pin counter */ - VTR_ASSERT(mem_out_pin_start_index == num_mems && mem_outb_pin_start_index == num_mems); + VTR_ASSERT(mem_out_pin_start_index == num_mems && + mem_outb_pin_start_index == num_mems); /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), @@ -1342,10 +1378,12 @@ int build_memory_group_module(ModuleManager& module_manager, * we just need to find all the I/O ports from the child modules and build a * list of it */ - ModuleManager::e_config_child_type config_child_type = ModuleManager::e_config_child_type::PHYSICAL; + ModuleManager::e_config_child_type config_child_type = + ModuleManager::e_config_child_type::PHYSICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); + module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type, + config_child_type); if (0 < module_num_config_bits) { add_sram_ports_to_module_manager(module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type, @@ -1356,10 +1394,11 @@ int build_memory_group_module(ModuleManager& module_manager, * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.num_configurable_children(mem_module, config_child_type)) { - add_module_nets_memory_config_bus(module_manager, decoder_lib, mem_module, - sram_orgz_type, - circuit_lib.design_tech_type(sram_model), config_child_type); + if (0 < + module_manager.num_configurable_children(mem_module, config_child_type)) { + add_module_nets_memory_config_bus( + module_manager, decoder_lib, mem_module, sram_orgz_type, + circuit_lib.design_tech_type(sram_model), config_child_type); } return CMD_EXEC_SUCCESS; @@ -1368,12 +1407,20 @@ int build_memory_group_module(ModuleManager& module_manager, /***************************************************************************** * This function creates a physical memory module and add it the current module * The following tasks will be accomplished: - * - Traverse all the logical configurable children in the module tree, starting from the current module - * - Build a list of the leaf logical configurable children and count the total memory sizes, the memory size for each physical memory submodule. Note that the physical memory submodule should be cached already in each leaf logical configurable children - * - Get the physical memory module required by each leaf logical configurable child - * - Create a dedicated module name for the physical memory (check if already exists, if yes, skip creating a new module) + * - Traverse all the logical configurable children in the module tree, starting + *from the current module + * - Build a list of the leaf logical configurable children and count the total + *memory sizes, the memory size for each physical memory submodule. Note that + *the physical memory submodule should be cached already in each leaf logical + *configurable children + * - Get the physical memory module required by each leaf logical configurable + *child + * - Create a dedicated module name for the physical memory (check if already + *exists, if yes, skip creating a new module) * - Instanciate the module - * - Built nets. Note that only the output ports of the physical memory block is required, since they should drive the dedicated memory ports of logical configurable children + * - Built nets. Note that only the output ports of the physical memory block is + *required, since they should drive the dedicated memory ports of logical + *configurable children *****************************************************************************/ int add_physical_memory_module(ModuleManager& module_manager, DecoderLibrary& decoder_lib, @@ -1384,49 +1431,70 @@ int add_physical_memory_module(ModuleManager& module_manager, int status = CMD_EXEC_SUCCESS; std::vector required_phy_mem_modules; - status = rec_find_physical_memory_children(static_cast(module_manager), curr_module, required_phy_mem_modules); + status = rec_find_physical_memory_children( + static_cast(module_manager), curr_module, + required_phy_mem_modules); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH, ModuleManager::e_config_child_type::LOGICAL); + module_manager, curr_module, circuit_lib, sram_model, + CONFIG_MEM_FEEDTHROUGH, ModuleManager::e_config_child_type::LOGICAL); /* No need to build a memory when there are no configuration bits required */ if (module_num_config_bits == 0) { return CMD_EXEC_SUCCESS; } - std::string phy_mem_module_name = generate_physical_memory_module_name(module_manager.module_name(curr_module), module_num_config_bits); + std::string phy_mem_module_name = generate_physical_memory_module_name( + module_manager.module_name(curr_module), module_num_config_bits); ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); if (!module_manager.valid_module_id(phy_mem_module)) { - status = build_memory_group_module(module_manager, decoder_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules, module_num_config_bits); + status = build_memory_group_module(module_manager, decoder_lib, circuit_lib, + sram_orgz_type, phy_mem_module_name, + sram_model, required_phy_mem_modules, + module_num_config_bits); } if (status != CMD_EXEC_SUCCESS) { - VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); + VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", + phy_mem_module_name.c_str()); return CMD_EXEC_FATAL_ERROR; } phy_mem_module = module_manager.find_module(phy_mem_module_name); if (!module_manager.valid_module_id(phy_mem_module)) { - VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str()); + VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", + phy_mem_module_name.c_str()); return CMD_EXEC_FATAL_ERROR; } /* Add the physical memory module to the current module */ - size_t phy_mem_instance = module_manager.num_instance(curr_module, phy_mem_module); + size_t phy_mem_instance = + module_manager.num_instance(curr_module, phy_mem_module); module_manager.add_child_module(curr_module, phy_mem_module, false); /* Register in the physical configurable children list */ - module_manager.add_configurable_child(curr_module, phy_mem_module, phy_mem_instance, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.add_configurable_child( + curr_module, phy_mem_module, phy_mem_instance, + ModuleManager::e_config_child_type::PHYSICAL); - /* Build nets between the data output of the physical memory module and the outputs of the logical configurable children */ + /* Build nets between the data output of the physical memory module and the + * outputs of the logical configurable children */ size_t curr_mem_pin_index = 0; std::map mem2mem_port_map; - mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); - mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); - for (size_t ichild = 0; ichild < module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL).size(); ++ichild) { - for (e_circuit_model_port_type port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { + mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = + std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); + mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = + std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); + for (size_t ichild = 0; + ichild < module_manager + .configurable_children( + curr_module, ModuleManager::e_config_child_type::PHYSICAL) + .size(); + ++ichild) { + for (e_circuit_model_port_type port_type : + {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { std::string src_port_name = mem2mem_port_map[port_type]; std::string des_port_name = - generate_sram_port_name(CONFIG_MEM_FEEDTHROUGH, port_type); + generate_sram_port_name(CONFIG_MEM_FEEDTHROUGH, port_type); /* Try to find these ports in the module manager */ ModulePortId src_port_id = module_manager.find_module_port(phy_mem_module, src_port_name); @@ -1436,15 +1504,16 @@ int add_physical_memory_module(ModuleManager& module_manager, BasicPort src_port = module_manager.module_port(phy_mem_module, src_port_id); - ModuleId des_module = module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; - size_t des_instance = module_manager.configurable_child_instances(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + ModuleId des_module = module_manager.configurable_children( + curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + size_t des_instance = module_manager.configurable_child_instances( + curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; ModulePortId des_port_id = module_manager.find_module_port(des_module, des_port_name); if (!module_manager.valid_module_port_id(des_module, des_port_id)) { return CMD_EXEC_FATAL_ERROR; } - BasicPort des_port = - module_manager.module_port(des_module, des_port_id); + BasicPort des_port = module_manager.module_port(des_module, des_port_id); /* Build nets */ for (size_t ipin = 0; ipin < des_port.pins().size(); ++ipin) { /* Create a net and add source and sink to it */ @@ -1463,17 +1532,28 @@ int add_physical_memory_module(ModuleManager& module_manager, } } - /* TODO: Recursively update the logical configurable child with the physical memory module parent and its instance id */ + /* TODO: Recursively update the logical configurable child with the physical + * memory module parent and its instance id */ std::map logical_mem_child_inst_count; - status = rec_update_logical_memory_children_with_physical_mapping(module_manager, curr_module, phy_mem_module, logical_mem_child_inst_count); + status = rec_update_logical_memory_children_with_physical_mapping( + module_manager, curr_module, phy_mem_module, logical_mem_child_inst_count); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } /* Sanity check */ std::map required_mem_child_inst_count; - for (ModuleId curr_child_module : module_manager.child_modules(phy_mem_module)) { - if (logical_mem_child_inst_count[curr_child_module] != module_manager.num_instance(phy_mem_module, curr_child_module)) { - VTR_LOG_ERROR("Expect the %lu instances of module '%s' under its parent '%s' while only updated %lu during logical-to-physical configurable child mapping sync-up!\n", module_manager.num_instance(phy_mem_module, curr_child_module), module_manager.module_name(curr_child_module).c_str(), module_manager.module_name(phy_mem_module).c_str(), logical_mem_child_inst_count[curr_child_module]); + for (ModuleId curr_child_module : + module_manager.child_modules(phy_mem_module)) { + if (logical_mem_child_inst_count[curr_child_module] != + module_manager.num_instance(phy_mem_module, curr_child_module)) { + VTR_LOG_ERROR( + "Expect the %lu instances of module '%s' under its parent '%s' while " + "only updated %lu during logical-to-physical configurable child " + "mapping sync-up!\n", + module_manager.num_instance(phy_mem_module, curr_child_module), + module_manager.module_name(curr_child_module).c_str(), + module_manager.module_name(phy_mem_module).c_str(), + logical_mem_child_inst_count[curr_child_module]); return CMD_EXEC_FATAL_ERROR; } } @@ -1481,5 +1561,4 @@ int add_physical_memory_module(ModuleManager& module_manager, return status; } - } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index 1ba7f52c3..cc9c03bb2 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -23,11 +23,11 @@ std::vector add_module_output_nets_to_chain_mem_modules( const size_t& child_index, const size_t& child_instance); int build_memory_modules(ModuleManager& module_manager, - DecoderLibrary& arch_decoder_lib, - const MuxLibrary& mux_lib, - const CircuitLibrary& circuit_lib, - const e_config_protocol_type& sram_orgz_type, - const bool& require_feedthrough_memory); + DecoderLibrary& arch_decoder_lib, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const bool& require_feedthrough_memory); int build_memory_group_module(ModuleManager& module_manager, DecoderLibrary& decoder_lib, diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index 1abc20a14..8aad4fea7 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -14,10 +14,10 @@ #include "vtr_time.h" /* Headers from openfpgautil library */ +#include "build_memory_modules.h" #include "build_module_graph_utils.h" #include "build_routing_module_utils.h" #include "build_routing_modules.h" -#include "build_memory_modules.h" #include "module_manager_utils.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" @@ -231,7 +231,8 @@ static void build_switch_block_mux_module( * modules */ std::string mem_instance_name = generate_sb_memory_instance_name( - SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string(""), group_config_block); + SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string(""), + group_config_block); module_manager.set_child_instance_name(sb_module, mem_module, mem_instance_id, mem_instance_name); @@ -241,16 +242,22 @@ static void build_switch_block_mux_module( module_manager, sb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ - size_t config_child_id = module_manager.num_configurable_children(sb_module, ModuleManager::e_config_child_type::LOGICAL); - module_manager.add_configurable_child(sb_module, mem_module, mem_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); + size_t config_child_id = module_manager.num_configurable_children( + sb_module, ModuleManager::e_config_child_type::LOGICAL); + module_manager.add_configurable_child( + sb_module, mem_module, mem_instance_id, + group_config_block ? ModuleManager::e_config_child_type::LOGICAL + : ModuleManager::e_config_child_type::UNIFIED); /* For logical memory, define the physical memory here */ if (group_config_block) { std::string physical_mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); - ModuleId physical_mem_module = module_manager.find_module(physical_mem_module_name); + ModuleId physical_mem_module = + module_manager.find_module(physical_mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(physical_mem_module)); - module_manager.set_logical2physical_configurable_child(sb_module, config_child_id, physical_mem_module); + module_manager.set_logical2physical_configurable_child( + sb_module, config_child_id, physical_mem_module); } } @@ -482,7 +489,8 @@ static void build_switch_block_module( /* Build a physical memory block */ if (group_config_block) { - add_physical_memory_module(module_manager, decoder_lib, sb_module, circuit_lib, sram_orgz_type, sram_model); + add_physical_memory_module(module_manager, decoder_lib, sb_module, + circuit_lib, sram_orgz_type, sram_model); } /* Add global ports to the pb_module: @@ -510,10 +518,13 @@ static void build_switch_block_module( * we just need to find all the I/O ports from the child modules and build a * list of it */ - ModuleManager::e_config_child_type config_child_type = group_config_block ? ModuleManager::e_config_child_type::PHYSICAL : ModuleManager::e_config_child_type::LOGICAL; + ModuleManager::e_config_child_type config_child_type = + group_config_block ? ModuleManager::e_config_child_type::PHYSICAL + : ModuleManager::e_config_child_type::LOGICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); + module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type, + config_child_type); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type, @@ -524,7 +535,8 @@ static void build_switch_block_module( * primitive modules This is a one-shot addition that covers all the memory * modules in this primitive module! */ - if (0 < module_manager.num_configurable_children(sb_module, config_child_type)) { + if (0 < + module_manager.num_configurable_children(sb_module, config_child_type)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, sb_module, sram_orgz_type, circuit_lib.design_tech_type(sram_model), config_child_type); @@ -726,7 +738,7 @@ static void build_connection_block_mux_module( if (group_config_block) { mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, - std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); } ModuleId mem_module = module_manager.find_module(mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); @@ -752,16 +764,22 @@ static void build_connection_block_mux_module( module_manager, cb_module, mux_module, mux_instance_id, mem_module, mem_instance_id, circuit_lib, mux_model); /* Update memory and instance list */ - size_t config_child_id = module_manager.num_configurable_children(cb_module, ModuleManager::e_config_child_type::LOGICAL); - module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id, group_config_block ? ModuleManager::e_config_child_type::LOGICAL : ModuleManager::e_config_child_type::UNIFIED); + size_t config_child_id = module_manager.num_configurable_children( + cb_module, ModuleManager::e_config_child_type::LOGICAL); + module_manager.add_configurable_child( + cb_module, mem_module, mem_instance_id, + group_config_block ? ModuleManager::e_config_child_type::LOGICAL + : ModuleManager::e_config_child_type::UNIFIED); /* For logical memory, define the physical memory here */ if (group_config_block) { std::string physical_mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); - ModuleId physical_mem_module = module_manager.find_module(physical_mem_module_name); + ModuleId physical_mem_module = + module_manager.find_module(physical_mem_module_name); VTR_ASSERT(true == module_manager.valid_module_id(physical_mem_module)); - module_manager.set_logical2physical_configurable_child(cb_module, config_child_id, physical_mem_module); + module_manager.set_logical2physical_configurable_child( + cb_module, config_child_id, physical_mem_module); } } @@ -795,8 +813,8 @@ static void build_connection_block_interc_modules( /* Print the multiplexer, fan_in >= 2 */ build_connection_block_mux_module( module_manager, cb_module, device_annotation, grids, rr_graph, rr_gsb, - cb_type, circuit_lib, cb_ipin_side, ipin_index, - input_port_to_module_nets, group_config_block); + cb_type, circuit_lib, cb_ipin_side, ipin_index, input_port_to_module_nets, + group_config_block); } /*Nothing should be done else*/ } @@ -861,8 +879,7 @@ static void build_connection_block_module( const RRGraphView& rr_graph, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, const RRGSB& rr_gsb, - const t_rr_type& cb_type, - const bool& group_config_block, + const t_rr_type& cb_type, const bool& group_config_block, const bool& verbose) { /* Create the netlist */ vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), @@ -985,13 +1002,15 @@ static void build_connection_block_module( ++inode) { build_connection_block_interc_modules( module_manager, cb_module, device_annotation, grids, rr_graph, rr_gsb, - cb_type, circuit_lib, cb_ipin_side, inode, input_port_to_module_nets, group_config_block); + cb_type, circuit_lib, cb_ipin_side, inode, input_port_to_module_nets, + group_config_block); } } /* Build a physical memory block */ if (group_config_block) { - add_physical_memory_module(module_manager, decoder_lib, cb_module, circuit_lib, sram_orgz_type, sram_model); + add_physical_memory_module(module_manager, decoder_lib, cb_module, + circuit_lib, sram_orgz_type, sram_model); } /* Add global ports to the pb_module: @@ -1019,10 +1038,13 @@ static void build_connection_block_module( * we just need to find all the I/O ports from the child modules and build a * list of it */ - ModuleManager::e_config_child_type config_child_type = group_config_block ? ModuleManager::e_config_child_type::PHYSICAL : ModuleManager::e_config_child_type::LOGICAL; + ModuleManager::e_config_child_type config_child_type = + group_config_block ? ModuleManager::e_config_child_type::PHYSICAL + : ModuleManager::e_config_child_type::LOGICAL; size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type, config_child_type); + module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type, + config_child_type); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type, @@ -1033,7 +1055,8 @@ static void build_connection_block_module( * primitive modules This is a one-shot addition that covers all the memory * modules in this primitive module! */ - if (0 < module_manager.num_configurable_children(cb_module, config_child_type)) { + if (0 < + module_manager.num_configurable_children(cb_module, config_child_type)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, cb_module, sram_orgz_type, circuit_lib.design_tech_type(sram_model), config_child_type); @@ -1052,8 +1075,7 @@ static void build_flatten_connection_block_modules( const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, const t_rr_type& cb_type, - const bool& group_config_block, - const bool& verbose) { + const bool& group_config_block, const bool& verbose) { /* Build unique X-direction connection block modules */ vtr::Point cb_range = device_rr_gsb.get_gsb_range(); @@ -1089,8 +1111,7 @@ void build_flatten_routing_modules( const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, - const bool& group_config_block, + const CircuitModelId& sram_model, const bool& group_config_block, const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build routing modules..."); @@ -1112,11 +1133,13 @@ void build_flatten_routing_modules( build_flatten_connection_block_modules( module_manager, decoder_lib, device_ctx, device_annotation, device_rr_gsb, - circuit_lib, sram_orgz_type, sram_model, CHANX, group_config_block, verbose); + circuit_lib, sram_orgz_type, sram_model, CHANX, group_config_block, + verbose); build_flatten_connection_block_modules( module_manager, decoder_lib, device_ctx, device_annotation, device_rr_gsb, - circuit_lib, sram_orgz_type, sram_model, CHANY, group_config_block, verbose); + circuit_lib, sram_orgz_type, sram_model, CHANY, group_config_block, + verbose); } /******************************************************************** @@ -1135,8 +1158,7 @@ void build_unique_routing_modules( const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, - const bool& group_config_block, + const CircuitModelId& sram_model, const bool& group_config_block, const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build unique routing modules..."); diff --git a/openfpga/src/fabric/build_routing_modules.h b/openfpga/src/fabric/build_routing_modules.h index e83f1a35d..038da3b3e 100644 --- a/openfpga/src/fabric/build_routing_modules.h +++ b/openfpga/src/fabric/build_routing_modules.h @@ -24,8 +24,7 @@ void build_flatten_routing_modules( const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, - const bool& group_config_block, + const CircuitModelId& sram_model, const bool& group_config_block, const bool& verbose); void build_unique_routing_modules( @@ -33,8 +32,7 @@ void build_unique_routing_modules( const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model, - const bool& group_config_block, + const CircuitModelId& sram_model, const bool& group_config_block, const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 43923ef91..88858cb0c 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1331,8 +1331,9 @@ static int build_tile_module( if (0 < find_module_num_config_bits(module_manager, pb_module, circuit_lib, sram_model, sram_orgz_type)) { - module_manager.add_configurable_child(tile_module, pb_module, - pb_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + tile_module, pb_module, pb_instance, + ModuleManager::e_config_child_type::UNIFIED); } VTR_LOGV( verbose, @@ -1378,8 +1379,9 @@ static int build_tile_module( if (0 < find_module_num_config_bits(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type)) { - module_manager.add_configurable_child(tile_module, cb_module, - cb_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + tile_module, cb_module, cb_instance, + ModuleManager::e_config_child_type::UNIFIED); } VTR_LOGV(verbose, "Added connection block module '%s' (instance: '%s') to " @@ -1417,8 +1419,9 @@ static int build_tile_module( sb_instance_name); if (0 < find_module_num_config_bits(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type)) { - module_manager.add_configurable_child(tile_module, sb_module, - sb_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + tile_module, sb_module, sb_instance, + ModuleManager::e_config_child_type::UNIFIED); } VTR_LOGV( verbose, @@ -1468,7 +1471,8 @@ static int build_tile_module( */ size_t module_num_config_bits = find_module_num_config_bits_from_child_modules( - module_manager, tile_module, circuit_lib, sram_model, sram_orgz_type, ModuleManager::e_config_child_type::LOGICAL); + module_manager, tile_module, circuit_lib, sram_model, sram_orgz_type, + ModuleManager::e_config_child_type::LOGICAL); if (0 < module_num_config_bits) { add_pb_sram_ports_to_module_manager(module_manager, tile_module, circuit_lib, sram_model, sram_orgz_type, @@ -1479,10 +1483,12 @@ static int build_tile_module( * This is a one-shot addition that covers all the memory modules in this pb * module! */ - if (0 < module_manager.num_configurable_children(tile_module, ModuleManager::e_config_child_type::LOGICAL)) { + if (0 < module_manager.num_configurable_children( + tile_module, ModuleManager::e_config_child_type::LOGICAL)) { add_pb_module_nets_memory_config_bus( module_manager, decoder_lib, tile_module, sram_orgz_type, - circuit_lib.design_tech_type(sram_model), ModuleManager::e_config_child_type::LOGICAL); + circuit_lib.design_tech_type(sram_model), + ModuleManager::e_config_child_type::LOGICAL); } VTR_LOGV(verbose, "Done\n"); diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index d8771f5de..ce133957a 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -134,7 +134,10 @@ int build_top_module( * module! */ if (false == frame_view) { - if (0 < module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size()) { + if (0 < module_manager + .configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL) + .size()) { add_top_module_nets_memory_config_bus( module_manager, decoder_lib, blwl_sr_banks, top_module, circuit_lib, config_protocol, circuit_lib.design_tech_type(sram_model), diff --git a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp index d32e8ac81..97c432899 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1076,7 +1076,11 @@ static void organize_top_module_tile_based_memory_modules( const CircuitModelId& sram_model, const DeviceGrid& grids, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { /* Ensure clean vectors to return */ - VTR_ASSERT(true == module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).empty()); + VTR_ASSERT(true == + module_manager + .configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL) + .empty()); std::vector> tile_coords; bool positive_direction = true; diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index cf6816b3f..4e0b45c98 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -86,8 +86,7 @@ static void organize_top_module_tile_cb_modules( module_manager.add_configurable_child( top_module, cb_module, cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)], - ModuleManager::e_config_child_type::UNIFIED, - config_coord); + ModuleManager::e_config_child_type::UNIFIED, config_coord); } } @@ -174,7 +173,8 @@ static void organize_top_module_tile_memory_modules( rr_gsb.get_sb_y() * 2 + 1); module_manager.add_configurable_child( top_module, sb_module, - sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], ModuleManager::e_config_child_type::UNIFIED, config_coord); + sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], + ModuleManager::e_config_child_type::UNIFIED, config_coord); } } @@ -221,8 +221,7 @@ static void organize_top_module_tile_memory_modules( module_manager.add_configurable_child( top_module, grid_module, grid_instance_ids[tile_coord.x()][tile_coord.y()], - ModuleManager::e_config_child_type::UNIFIED, - config_coord); + ModuleManager::e_config_child_type::UNIFIED, config_coord); } } @@ -272,14 +271,21 @@ void build_top_module_configurable_regions( "Build configurable regions for the top module"); /* Ensure we have valid configurable children */ - VTR_ASSERT(false == module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).empty()); + VTR_ASSERT(false == + module_manager + .configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL) + .empty()); /* Ensure that our region definition is valid */ VTR_ASSERT(1 <= config_protocol.num_regions()); /* Exclude decoders from the list */ size_t num_configurable_children = - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); if (CONFIG_MEM_MEMORY_BANK == config_protocol.type() || CONFIG_MEM_QL_MEMORY_BANK == config_protocol.type()) { num_configurable_children -= 2; @@ -294,7 +300,10 @@ void build_top_module_configurable_regions( bool create_region = true; ConfigRegionId curr_region = ConfigRegionId::INVALID(); for (size_t ichild = 0; - ichild < module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + ichild < module_manager + .configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL) + .size(); ++ichild) { if (true == create_region) { curr_region = module_manager.add_config_region(top_module); @@ -303,8 +312,11 @@ void build_top_module_configurable_regions( /* Add the child to a region */ module_manager.add_configurable_child_to_region( top_module, curr_region, - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild], - module_manager.configurable_child_instances(top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild], ichild); + module_manager.configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild], + module_manager.configurable_child_instances( + top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild], + ichild); /* See if the current region is full or not: * For the last region, we will keep adding until we finish all the children @@ -430,7 +442,11 @@ void organize_top_module_memory_modules( const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy) { /* Ensure clean vectors to return */ - VTR_ASSERT(true == module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).empty()); + VTR_ASSERT(true == + module_manager + .configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL) + .empty()); /* First, organize the I/O tiles on the border */ /* Special for the I/O tileas on RIGHT and BOTTOM, @@ -533,7 +549,11 @@ void organize_top_module_memory_modules( void shuffle_top_module_configurable_children( ModuleManager& module_manager, const ModuleId& top_module, const ConfigProtocol& config_protocol) { - size_t num_keys = module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + size_t num_keys = + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); std::vector shuffled_keys; shuffled_keys.reserve(num_keys); for (size_t ikey = 0; ikey < num_keys; ++ikey) { @@ -544,11 +564,14 @@ void shuffle_top_module_configurable_children( /* Cache the configurable children and their instances */ std::vector orig_configurable_children = - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL); std::vector orig_configurable_child_instances = - module_manager.configurable_child_instances(top_module, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.configurable_child_instances( + top_module, ModuleManager::e_config_child_type::PHYSICAL); std::vector> orig_configurable_child_coordinates = - module_manager.configurable_child_coordinates(top_module, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.configurable_child_coordinates( + top_module, ModuleManager::e_config_child_type::PHYSICAL); /* Reorganize the configurable children */ module_manager.clear_configurable_children(top_module); @@ -654,10 +677,10 @@ int load_top_module_memory_modules_from_fabric_key( } /* Now we can add the child to configurable children of the top module */ - module_manager.add_configurable_child(top_module, instance_info.first, - instance_info.second, - ModuleManager::e_config_child_type::UNIFIED, - fabric_key.key_coordinate(key)); + module_manager.add_configurable_child( + top_module, instance_info.first, instance_info.second, + ModuleManager::e_config_child_type::UNIFIED, + fabric_key.key_coordinate(key)); module_manager.add_configurable_child_to_region( top_module, top_module_config_region, instance_info.first, instance_info.second, curr_configurable_child_id); @@ -1334,17 +1357,27 @@ static void add_top_module_nets_cmos_memory_bank_config_bus( * Note: this MUST be done after adding all the module nets to other regular * configurable children */ - module_manager.add_configurable_child(top_module, bl_decoder_module, - curr_bl_decoder_instance_id, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.add_configurable_child( + top_module, bl_decoder_module, curr_bl_decoder_instance_id, + ModuleManager::e_config_child_type::PHYSICAL); module_manager.add_configurable_child_to_region( top_module, config_region, bl_decoder_module, curr_bl_decoder_instance_id, - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size() - + 1); - module_manager.add_configurable_child(top_module, wl_decoder_module, - curr_wl_decoder_instance_id, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.add_configurable_child( + top_module, wl_decoder_module, curr_wl_decoder_instance_id, + ModuleManager::e_config_child_type::PHYSICAL); module_manager.add_configurable_child_to_region( top_module, config_region, wl_decoder_module, curr_wl_decoder_instance_id, - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size() - + 1); } } @@ -1764,8 +1797,8 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( 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, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[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 = @@ -1799,8 +1832,8 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( 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, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; ModulePortId child_en_port = module_manager.find_module_port( child_module, std::string(DECODER_ENABLE_PORT_NAME)); BasicPort child_en_port_info = @@ -1819,12 +1852,17 @@ static void add_top_module_nets_cmos_memory_frame_decoder_config_bus( } /* Add the decoder as the last configurable children */ - module_manager.add_configurable_child(parent_module, decoder_module, - decoder_instance, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.add_configurable_child( + parent_module, decoder_module, decoder_instance, + ModuleManager::e_config_child_type::PHYSICAL); /* 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, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size() - + 1); } /********************************************************************* diff --git a/openfpga/src/fabric/build_top_module_memory_bank.cpp b/openfpga/src/fabric/build_top_module_memory_bank.cpp index d0876add8..62b12f254 100644 --- a/openfpga/src/fabric/build_top_module_memory_bank.cpp +++ b/openfpga/src/fabric/build_top_module_memory_bank.cpp @@ -58,7 +58,11 @@ static void add_module_nets_to_ql_memory_bank_shift_register_module( const std::string& chain_head_port_name, const std::string& chain_tail_port_name) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + mem_index < + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -78,28 +82,30 @@ static void add_module_nets_to_ql_memory_bank_shift_register_module( /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); - net_sink_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); - net_src_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index - 1]; + net_src_module_id = module_manager.configurable_children( + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( - parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index - 1]; + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[mem_index - 1]; net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); /* Find the port name of next memory module */ std::string sink_port_name = circuit_lib.port_prefix(model_input_port); - net_sink_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -133,9 +139,15 @@ static void add_module_nets_to_ql_memory_bank_shift_register_module( /* Find the port name of previous memory module */ std::string src_port_name = circuit_lib.port_prefix(model_output_port); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).back(); + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL).back(); + module_manager + .configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL) + .back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -244,8 +256,9 @@ static ModuleId build_bl_shift_register_chain_module( size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); - module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + mem_module, sram_mem_module, sram_mem_instance, + ModuleManager::e_config_child_type::UNIFIED); /* Build module nets to wire bl outputs of sram modules to BL outputs of * memory module */ @@ -362,8 +375,9 @@ static ModuleId build_wl_shift_register_chain_module( size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module); module_manager.add_child_module(mem_module, sram_mem_module); - module_manager.add_configurable_child(mem_module, sram_mem_module, - sram_mem_instance, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + mem_module, sram_mem_module, sram_mem_instance, + ModuleManager::e_config_child_type::UNIFIED); /* Build module nets to wire wl outputs of sram modules to WL outputs of * memory module */ @@ -698,11 +712,16 @@ static void add_top_module_nets_cmos_ql_memory_bank_bl_decoder_config_bus( * Note: this MUST be done after adding all the module nets to other regular * configurable children */ - module_manager.add_configurable_child(top_module, bl_decoder_module, - curr_bl_decoder_instance_id, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + top_module, bl_decoder_module, curr_bl_decoder_instance_id, + ModuleManager::e_config_child_type::UNIFIED); module_manager.add_configurable_child_to_region( top_module, config_region, bl_decoder_module, curr_bl_decoder_instance_id, - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size() - + 1); } } @@ -967,11 +986,16 @@ static void add_top_module_nets_cmos_ql_memory_bank_wl_decoder_config_bus( * Note: this MUST be done after adding all the module nets to other regular * configurable children */ - module_manager.add_configurable_child(top_module, wl_decoder_module, - curr_wl_decoder_instance_id, ModuleManager::e_config_child_type::UNIFIED); + module_manager.add_configurable_child( + top_module, wl_decoder_module, curr_wl_decoder_instance_id, + ModuleManager::e_config_child_type::UNIFIED); module_manager.add_configurable_child_to_region( top_module, config_region, wl_decoder_module, curr_wl_decoder_instance_id, - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size() - 1); + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size() - + 1); } } diff --git a/openfpga/src/fabric/fabric_key_writer.cpp b/openfpga/src/fabric/fabric_key_writer.cpp index f452ec6f6..cef0a79d4 100644 --- a/openfpga/src/fabric/fabric_key_writer.cpp +++ b/openfpga/src/fabric/fabric_key_writer.cpp @@ -32,7 +32,10 @@ static int add_module_keys_to_fabric_key(const ModuleManager& module_manager, return CMD_EXEC_SUCCESS; } /* Bypass modules which does not have any configurable children */ - if (module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL).empty()) { + if (module_manager + .configurable_children(curr_module, + ModuleManager::e_config_child_type::PHYSICAL) + .empty()) { return CMD_EXEC_SUCCESS; } /* Now create the module and add subkey one by one */ @@ -41,12 +44,15 @@ static int add_module_keys_to_fabric_key(const ModuleManager& module_manager, return CMD_EXEC_FATAL_ERROR; } size_t num_config_child = - module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + module_manager + .configurable_children(curr_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); for (size_t ichild = 0; ichild < num_config_child; ++ichild) { - ModuleId child_module = - module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; - size_t child_instance = - module_manager.configurable_child_instances(curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + ModuleId child_module = module_manager.configurable_children( + curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + size_t child_instance = module_manager.configurable_child_instances( + curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; FabricSubKeyId sub_key = fabric_key.create_module_key(key_module_id); fabric_key.set_sub_key_name(sub_key, @@ -111,7 +117,11 @@ int write_fabric_key_to_xml_file( /* Build a fabric key database by visiting all the configurable children */ FabricKey fabric_key; - size_t num_keys = module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + size_t num_keys = + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); fabric_key.reserve_keys(num_keys); diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index e91030e9f..e71378ce1 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -84,7 +84,7 @@ std::vector ModuleManager::configurable_children( if (type == ModuleManager::e_config_child_type::LOGICAL) { return logical_configurable_children_[parent_module]; - } + } VTR_ASSERT(type == ModuleManager::e_config_child_type::PHYSICAL); return physical_configurable_children_[parent_module]; } @@ -121,7 +121,8 @@ std::vector ModuleManager::logical2physical_configurable_children( } /* Find all the instances of configurable child modules under a parent module */ -std::vector ModuleManager::logical2physical_configurable_child_instances( +std::vector +ModuleManager::logical2physical_configurable_child_instances( const ModuleId& parent_module) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); @@ -130,7 +131,8 @@ std::vector ModuleManager::logical2physical_configurable_child_instances } /* Find all the instances of configurable child modules under a parent module */ -std::vector ModuleManager::logical2physical_configurable_child_parents( +std::vector +ModuleManager::logical2physical_configurable_child_parents( const ModuleId& parent_module) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); @@ -412,7 +414,8 @@ size_t ModuleManager::instance_id(const ModuleId& parent_module, return size_t(-1); } -size_t ModuleManager::num_configurable_children(const ModuleId& parent_module, const e_config_child_type& type) const { +size_t ModuleManager::num_configurable_children( + const ModuleId& parent_module, const e_config_child_type& type) const { VTR_ASSERT(valid_module_id(parent_module)); if (type == ModuleManager::e_config_child_type::LOGICAL) { return logical_configurable_children_[parent_module].size(); @@ -657,15 +660,20 @@ bool ModuleManager::net_sink_exist(const ModuleId& module, return false; } -bool ModuleManager::unified_configurable_children(const ModuleId& curr_module) const { - if (logical_configurable_children_[curr_module].size() != physical_configurable_children_[curr_module].size()) { +bool ModuleManager::unified_configurable_children( + const ModuleId& curr_module) const { + if (logical_configurable_children_[curr_module].size() != + physical_configurable_children_[curr_module].size()) { return false; } - for (size_t ichild = 0; ichild < logical_configurable_children_[curr_module].size(); ++ichild) { - if (logical_configurable_children_[curr_module][ichild] != physical_configurable_children_[curr_module][ichild]) { + for (size_t ichild = 0; + ichild < logical_configurable_children_[curr_module].size(); ++ichild) { + if (logical_configurable_children_[curr_module][ichild] != + physical_configurable_children_[curr_module][ichild]) { return false; } - if (logical_configurable_child_instances_[curr_module][ichild] != physical_configurable_child_instances_[curr_module][ichild]) { + if (logical_configurable_child_instances_[curr_module][ichild] != + physical_configurable_child_instances_[curr_module][ichild]) { return false; } } @@ -977,88 +985,129 @@ void ModuleManager::add_configurable_child(const ModuleId& parent_module, /* Ensure that the instance id is in range */ VTR_ASSERT(child_instance < num_instance(parent_module, child_module)); - if (type == ModuleManager::e_config_child_type::LOGICAL || type == ModuleManager::e_config_child_type::UNIFIED) { + if (type == ModuleManager::e_config_child_type::LOGICAL || + type == ModuleManager::e_config_child_type::UNIFIED) { logical_configurable_children_[parent_module].push_back(child_module); - logical_configurable_child_instances_[parent_module].push_back(child_instance); + logical_configurable_child_instances_[parent_module].push_back( + child_instance); } - if (type == ModuleManager::e_config_child_type::PHYSICAL || type == ModuleManager::e_config_child_type::UNIFIED) { + if (type == ModuleManager::e_config_child_type::PHYSICAL || + type == ModuleManager::e_config_child_type::UNIFIED) { physical_configurable_children_[parent_module].push_back(child_module); - physical_configurable_child_instances_[parent_module].push_back(child_instance); + physical_configurable_child_instances_[parent_module].push_back( + child_instance); physical_configurable_child_regions_[parent_module].push_back( ConfigRegionId::INVALID()); physical_configurable_child_coordinates_[parent_module].push_back(coord); } if (type == ModuleManager::e_config_child_type::UNIFIED) { - logical2physical_configurable_children_[parent_module].push_back(child_module); - logical2physical_configurable_child_instances_[parent_module].push_back(child_instance); - logical2physical_configurable_child_parents_[parent_module].push_back(parent_module); + logical2physical_configurable_children_[parent_module].push_back( + child_module); + logical2physical_configurable_child_instances_[parent_module].push_back( + child_instance); + logical2physical_configurable_child_parents_[parent_module].push_back( + parent_module); } else if (type == ModuleManager::e_config_child_type::LOGICAL) { logical2physical_configurable_children_[parent_module].emplace_back(); - logical2physical_configurable_child_instances_[parent_module].emplace_back(); + logical2physical_configurable_child_instances_[parent_module] + .emplace_back(); logical2physical_configurable_child_parents_[parent_module].emplace_back(); } } -void ModuleManager::set_logical2physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module) { +void ModuleManager::set_logical2physical_configurable_child( + const ModuleId& parent_module, const size_t& logical_child_id, + const ModuleId& physical_child_module) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(logical_child_id < num_configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)); + VTR_ASSERT(logical_child_id < + num_configurable_children( + parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ - logical2physical_configurable_children_[parent_module][logical_child_id] = physical_child_module; + logical2physical_configurable_children_[parent_module][logical_child_id] = + physical_child_module; } -void ModuleManager::set_logical2physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance) { +void ModuleManager::set_logical2physical_configurable_child_instance( + const ModuleId& parent_module, const size_t& logical_child_id, + const size_t& physical_child_instance) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(logical_child_id < num_configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)); + VTR_ASSERT(logical_child_id < + num_configurable_children( + parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ - logical2physical_configurable_child_instances_[parent_module][logical_child_id] = physical_child_instance; + logical2physical_configurable_child_instances_[parent_module] + [logical_child_id] = + physical_child_instance; } -void ModuleManager::set_logical2physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module) { +void ModuleManager::set_logical2physical_configurable_child_parent_module( + const ModuleId& parent_module, const size_t& logical_child_id, + const ModuleId& physical_child_parent_module) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(logical_child_id < num_configurable_children(parent_module, ModuleManager::e_config_child_type::LOGICAL)); + VTR_ASSERT(logical_child_id < + num_configurable_children( + parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ - logical2physical_configurable_child_parents_[parent_module][logical_child_id] = physical_child_parent_module; + logical2physical_configurable_child_parents_[parent_module] + [logical_child_id] = + physical_child_parent_module; } -void ModuleManager::reserve_configurable_child(const ModuleId& parent_module, - const size_t& num_children, - const e_config_child_type& type) { +void ModuleManager::reserve_configurable_child( + const ModuleId& parent_module, const size_t& num_children, + const e_config_child_type& type) { VTR_ASSERT(valid_module_id(parent_module)); - if (type == ModuleManager::e_config_child_type::LOGICAL || type == ModuleManager::e_config_child_type::UNIFIED) { - /* Do reserve when the number of children is larger than current size of lists + if (type == ModuleManager::e_config_child_type::LOGICAL || + type == ModuleManager::e_config_child_type::UNIFIED) { + /* Do reserve when the number of children is larger than current size of + * lists */ if (num_children > logical_configurable_children_[parent_module].size()) { logical_configurable_children_[parent_module].reserve(num_children); } - if (num_children > logical_configurable_child_instances_[parent_module].size()) { - logical_configurable_child_instances_[parent_module].reserve(num_children); + if (num_children > + logical_configurable_child_instances_[parent_module].size()) { + logical_configurable_child_instances_[parent_module].reserve( + num_children); } - if (num_children > logical2physical_configurable_children_[parent_module].size()) { - logical2physical_configurable_children_[parent_module].reserve(num_children); + if (num_children > + logical2physical_configurable_children_[parent_module].size()) { + logical2physical_configurable_children_[parent_module].reserve( + num_children); } - if (num_children > logical2physical_configurable_child_instances_[parent_module].size()) { - logical2physical_configurable_child_instances_[parent_module].reserve(num_children); + if (num_children > + logical2physical_configurable_child_instances_[parent_module].size()) { + logical2physical_configurable_child_instances_[parent_module].reserve( + num_children); } - if (num_children > logical2physical_configurable_child_parents_[parent_module].size()) { - logical2physical_configurable_child_parents_[parent_module].reserve(num_children); + if (num_children > + logical2physical_configurable_child_parents_[parent_module].size()) { + logical2physical_configurable_child_parents_[parent_module].reserve( + num_children); } } - if (type == ModuleManager::e_config_child_type::PHYSICAL || type == ModuleManager::e_config_child_type::UNIFIED) { + if (type == ModuleManager::e_config_child_type::PHYSICAL || + type == ModuleManager::e_config_child_type::UNIFIED) { if (num_children > physical_configurable_children_[parent_module].size()) { physical_configurable_children_[parent_module].reserve(num_children); } - if (num_children > physical_configurable_child_instances_[parent_module].size()) { - physical_configurable_child_instances_[parent_module].reserve(num_children); + if (num_children > + physical_configurable_child_instances_[parent_module].size()) { + physical_configurable_child_instances_[parent_module].reserve( + num_children); } - if (num_children > physical_configurable_child_regions_[parent_module].size()) { + if (num_children > + physical_configurable_child_regions_[parent_module].size()) { physical_configurable_child_regions_[parent_module].reserve(num_children); } - if (num_children > physical_configurable_child_coordinates_[parent_module].size()) { - physical_configurable_child_coordinates_[parent_module].reserve(num_children); + if (num_children > + physical_configurable_child_coordinates_[parent_module].size()) { + physical_configurable_child_coordinates_[parent_module].reserve( + num_children); } } } @@ -1088,15 +1137,19 @@ void ModuleManager::add_configurable_child_to_region( /* Ensure that the child module is in the configurable children list */ VTR_ASSERT(child_module == - configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[config_child_id]); + configurable_children( + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[config_child_id]); VTR_ASSERT(child_instance == - configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[config_child_id]); + configurable_child_instances( + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[config_child_id]); /* If the child is already in another region, error out */ - if ((true == - valid_region_id( - parent_module, - physical_configurable_child_regions_[parent_module][config_child_id])) && + if ((true == valid_region_id( + parent_module, + physical_configurable_child_regions_[parent_module] + [config_child_id])) && (config_region != physical_configurable_child_regions_[parent_module][config_child_id])) { VTR_LOGF_ERROR( @@ -1104,7 +1157,8 @@ void ModuleManager::add_configurable_child_to_region( "Try to add a configurable child '%s[%lu]' to region '%lu' which is " "already added to another region '%lu'!\n", module_name(child_module).c_str(), child_instance, size_t(config_region), - size_t(physical_configurable_child_regions_[parent_module][config_child_id])); + size_t( + physical_configurable_child_regions_[parent_module][config_child_id])); exit(1); } diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 2c6f1fc7c..02f8dfb5b 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -68,17 +68,14 @@ class ModuleManager { NUM_MODULE_USAGE_TYPES }; - /* Type of configurable child: - * - logical: represent a logical configurable block, which may not contain a physical memory inside - * - physical: represent a physical configurable block, which contains a physical memory inside + /* Type of configurable child: + * - logical: represent a logical configurable block, which may not contain a + * physical memory inside + * - physical: represent a physical configurable block, which contains a + * physical memory inside * - unified: a unified block whose physical memory is also the logical memory */ - enum class e_config_child_type { - LOGICAL, - PHYSICAL, - UNIFIED, - NUM_TYPES - }; + enum class e_config_child_type { LOGICAL, PHYSICAL, UNIFIED, NUM_TYPES }; public: /* Public Constructors */ public: /* Type implementations */ @@ -196,8 +193,10 @@ class ModuleManager { */ std::vector logical2physical_configurable_child_instances( const ModuleId& parent_module) const; - /* Find all the parent modules of physical configurable child modules under a parent module - * Note that a physical configurable child module may be at another module; Only the logical child module is under the current parent module + /* Find all the parent modules of physical configurable child modules under a + * parent module Note that a physical configurable child module may be at + * another module; Only the logical child module is under the current parent + * module */ std::vector logical2physical_configurable_child_parents( const ModuleId& parent_module) const; @@ -265,7 +264,8 @@ class ModuleManager { const ModuleId& child_module, const std::string& instance_name) const; /** @brief Count the number of logical configurable children */ - size_t num_configurable_children(const ModuleId& parent_module, const e_config_child_type& type) const; + size_t num_configurable_children(const ModuleId& parent_module, + const e_config_child_type& type) const; /* Find the type of a port */ ModuleManager::e_module_port_type port_type(const ModuleId& module, const ModulePortId& port) const; @@ -323,7 +323,9 @@ class ModuleManager { const ModuleId& sink_module, const size_t& instance_id, const ModulePortId& sink_port, const size_t& sink_pin); - /** @brief Check if the configurable children under a given module are unified or not. If unified, it means that the logical configurable children are the same as the physical configurable children */ + /** @brief Check if the configurable children under a given module are unified + * or not. If unified, it means that the logical configurable children are the + * same as the physical configurable children */ bool unified_configurable_children(const ModuleId& curr_module) const; private: /* Private accessors */ @@ -388,14 +390,21 @@ class ModuleManager { */ void add_configurable_child( const ModuleId& module, const ModuleId& child_module, - const size_t& child_instance, - const e_config_child_type& type, + const size_t& child_instance, const e_config_child_type& type, const vtr::Point coord = vtr::Point(-1, -1)); - /** @brief Create a pair of mapping from a logical configurable child to a physical configurable child */ - void set_logical2physical_configurable_child(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_module); - /** @brief Create a pair of mapping from a logical configurable child to a physical configurable child */ - void set_logical2physical_configurable_child_instance(const ModuleId& parent_module, const size_t& logical_child_id, const size_t& physical_child_instance); - void set_logical2physical_configurable_child_parent_module(const ModuleId& parent_module, const size_t& logical_child_id, const ModuleId& physical_child_parent_module); + /** @brief Create a pair of mapping from a logical configurable child to a + * physical configurable child */ + void set_logical2physical_configurable_child( + const ModuleId& parent_module, const size_t& logical_child_id, + const ModuleId& physical_child_module); + /** @brief Create a pair of mapping from a logical configurable child to a + * physical configurable child */ + void set_logical2physical_configurable_child_instance( + const ModuleId& parent_module, const size_t& logical_child_id, + const size_t& physical_child_instance); + void set_logical2physical_configurable_child_parent_module( + const ModuleId& parent_module, const size_t& logical_child_id, + const ModuleId& physical_child_parent_module); /* Reserved a number of configurable children for memory efficiency */ void reserve_configurable_child(const ModuleId& module, const size_t& num_children, @@ -405,8 +414,8 @@ class ModuleManager { ConfigRegionId add_config_region(const ModuleId& module); /* Add a configurable child module to a region * Note: - * - The child module must be added as a physical configurable child to the parent - * module before calling this function! + * - The child module must be added as a physical configurable child to the + * parent module before calling this function! */ void add_configurable_child_to_region(const ModuleId& parent_module, const ConfigRegionId& config_region, @@ -550,41 +559,49 @@ class ModuleManager { * from the children_ list This is really dependent how the configuration * protocol is organized which should be made by users/designers * Note that there could be two types of configurable children under a module - * - logical: only contains virtual/feedthough memory blocks. A logical configurable child can only contain logical subchild. Logical memory block is required for architecture bitstream generation, because it carries logical information (the location of memory to its programmable resources) - * - physical: contains physical memory blocks. Logical memory blocks are mapped to the physical memory block. A physical memory block may contain coordinates and configuration regions which are required for fabric bitstream generation. + * - logical: only contains virtual/feedthough memory blocks. A logical + * configurable child can only contain logical subchild. Logical memory block + * is required for architecture bitstream generation, because it carries + * logical information (the location of memory to its programmable resources) + * - physical: contains physical memory blocks. Logical memory blocks are + * mapped to the physical memory block. A physical memory block may contain + * coordinates and configuration regions which are required for fabric + * bitstream generation. */ vtr::vector> - logical_configurable_children_; /* Child modules with configurable memory bits that - this module contain */ + logical_configurable_children_; /* Child modules with configurable memory + bits that this module contain */ vtr::vector> logical_configurable_child_instances_; /* Instances of child modules with configurable memory bits that this module contain */ vtr::vector> - logical2physical_configurable_children_; /* Child modules with configurable memory bits that - this module contain */ + logical2physical_configurable_children_; /* Child modules with configurable + memory bits that this module contain */ vtr::vector> - logical2physical_configurable_child_instances_; /* Instances of child modules with - configurable memory bits that this module - contain */ + logical2physical_configurable_child_instances_; /* Instances of child + modules with configurable memory bits that + this module contain */ vtr::vector> - logical2physical_configurable_child_parents_; /* Parent modules with configurable memory bits that - this module contain */ + logical2physical_configurable_child_parents_; /* Parent modules with + configurable memory bits that this module contain + */ vtr::vector> - physical_configurable_children_; /* Child modules with configurable memory bits that - this module contain */ + physical_configurable_children_; /* Child modules with configurable memory + bits that this module contain */ vtr::vector> physical_configurable_child_instances_; /* Instances of child modules with configurable memory bits that this module contain */ vtr::vector> - physical_configurable_child_regions_; /* Instances of child modules with configurable - memory bits that this module contain */ + physical_configurable_child_regions_; /* Instances of child modules with + configurable memory bits that this module + contain */ vtr::vector>> - physical_configurable_child_coordinates_; /* Relative coorindates of child modules - with configurable memory bits that this - module contain */ + physical_configurable_child_coordinates_; /* Relative coorindates of child + modules with configurable memory bits + that this module contain */ /* Configurable regions to group the physical configurable children * Note: diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 82f2b9dcd..d4a15986f 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -35,15 +35,19 @@ static size_t rec_estimate_device_bitstream_num_blocks( * actually configurable memory elements * We skip them in couting */ - if (0 == module_manager.num_configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL)) { + if (0 == module_manager.num_configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL)) { return 0; } size_t num_configurable_children = - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + module_manager + .configurable_children(top_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); for (size_t ichild = 0; ichild < num_configurable_children; ++ichild) { - ModuleId child_module = - module_manager.configurable_children(top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + ModuleId child_module = module_manager.configurable_children( + top_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; num_blocks += rec_estimate_device_bitstream_num_blocks(module_manager, child_module); } @@ -68,7 +72,8 @@ static size_t rec_estimate_device_bitstream_num_bits( /* 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.num_configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)) { + if (0 == module_manager.num_configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)) { return 1; } @@ -105,7 +110,10 @@ static size_t rec_estimate_device_bitstream_num_bits( VTR_ASSERT_SAFE(parent_module != top_module); size_t num_configurable_children = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); /* Frame-based configuration protocol will have 1 decoder * if there are more than 1 configurable children @@ -116,8 +124,8 @@ static size_t rec_estimate_device_bitstream_num_bits( } for (size_t ichild = 0; ichild < num_configurable_children; ++ichild) { - ModuleId child_module = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + ModuleId child_module = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; num_bits += rec_estimate_device_bitstream_num_bits( module_manager, top_module, child_module, config_protocol); } @@ -193,7 +201,8 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, /* 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, ModuleManager::e_config_child_type::PHYSICAL)); + openfpga_ctx.module_graph(), top_module, + ModuleManager::e_config_child_type::PHYSICAL)); /* Create bitstream from grids */ VTR_LOGV(verbose, "Building grid bitstream...\n"); diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index 4a9d17406..edf20681c 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -74,12 +74,17 @@ static void rec_build_module_fabric_dependent_chain_bitstream( } else { for (size_t child_id = 0; child_id < - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + module_manager + .configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL) + .size(); ++child_id) { - ModuleId child_module = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_id]; - size_t child_instance = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_id]; + ModuleId child_module = module_manager.configurable_children( + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[child_id]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[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); @@ -196,7 +201,8 @@ static void rec_build_module_fabric_dependent_memory_bank_bitstream( * - no need to exclude decoders as they are not there */ std::vector configurable_children = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL); size_t num_configurable_children = configurable_children.size(); @@ -211,8 +217,9 @@ static void rec_build_module_fabric_dependent_memory_bank_bitstream( 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, ModuleManager::e_config_child_type::PHYSICAL)[child_id]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[child_id]; /* Get the instance name and ensure it is not empty */ std::string instance_name = module_manager.instance_name( @@ -323,10 +330,11 @@ static void rec_build_module_fabric_dependent_frame_bitstream( config_region); } else { VTR_ASSERT(top_module != parent_module); - configurable_children = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL); + configurable_children = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL); configurable_child_instances = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL); } size_t num_configurable_children = configurable_children.size(); @@ -360,10 +368,13 @@ static void rec_build_module_fabric_dependent_frame_bitstream( /* 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, ModuleManager::e_config_child_type::PHYSICAL)) { + for (const ModuleId& child_module : module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)) { /* Bypass any decoder module (which no configurable children */ - if (module_manager.configurable_children(child_module, ModuleManager::e_config_child_type::PHYSICAL).empty()) { + if (module_manager + .configurable_children( + child_module, ModuleManager::e_config_child_type::PHYSICAL) + .empty()) { continue; } const ModulePortId& child_addr_port_id = @@ -493,8 +504,8 @@ static void rec_build_module_fabric_dependent_frame_bitstream( parent_modules.back(), config_region); } else { VTR_ASSERT(top_module != parent_modules.back()); - configurable_children = - module_manager.configurable_children(parent_modules.back(), ModuleManager::e_config_child_type::PHYSICAL); + configurable_children = module_manager.configurable_children( + parent_modules.back(), ModuleManager::e_config_child_type::PHYSICAL); } ModuleId decoder_module = configurable_children.back(); diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp index a6e0efdac..8507bd199 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp @@ -123,7 +123,8 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( * - no need to exclude decoders as they are not there */ std::vector configurable_children = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL); + module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL); size_t num_configurable_children = configurable_children.size(); @@ -138,8 +139,9 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( 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, ModuleManager::e_config_child_type::PHYSICAL)[child_id]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, + ModuleManager::e_config_child_type::PHYSICAL)[child_id]; /* Get the instance name and ensure it is not empty */ std::string instance_name = module_manager.instance_name( diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index fa3f34613..cff41f1ec 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -636,7 +636,8 @@ static void rec_build_physical_block_bitstream( VTR_ASSERT(true == module_manager.valid_module_id(pb_module)); /* Skip module with no configurable children */ - if (0 == module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::LOGICAL)) { + if (0 == module_manager.num_configurable_children( + pb_module, ModuleManager::e_config_child_type::LOGICAL)) { return; } @@ -644,17 +645,20 @@ static void rec_build_physical_block_bitstream( * manager */ std::string pb_block_name = generate_physical_block_instance_name( physical_pb_type, pb_graph_node_index); - /* If there are no physical memory blocks under the current module, use the previous module, which is the physical memory block */ + /* If there are no physical memory blocks under the current module, use the + * previous module, which is the physical memory block */ ConfigBlockId pb_configurable_block = parent_configurable_block; - if (0 < module_manager.num_configurable_children(pb_module, ModuleManager::e_config_child_type::PHYSICAL)) { + if (0 < module_manager.num_configurable_children( + pb_module, ModuleManager::e_config_child_type::PHYSICAL)) { pb_configurable_block = bitstream_manager.add_block(pb_block_name); bitstream_manager.add_child_block(parent_configurable_block, pb_configurable_block); /* Reserve child blocks for new created block */ bitstream_manager.reserve_child_blocks( parent_configurable_block, - count_module_manager_module_configurable_children(module_manager, - pb_module, ModuleManager::e_config_child_type::PHYSICAL)); + count_module_manager_module_configurable_children( + module_manager, pb_module, + ModuleManager::e_config_child_type::PHYSICAL)); } /* Recursively finish all the child pb_types*/ @@ -750,7 +754,8 @@ static void build_physical_block_bitstream( VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); /* Skip module with no configurable children */ - if (0 == module_manager.num_configurable_children(grid_module, ModuleManager::e_config_child_type::LOGICAL)) { + if (0 == module_manager.num_configurable_children( + grid_module, ModuleManager::e_config_child_type::LOGICAL)) { return; } @@ -772,14 +777,26 @@ static void build_physical_block_bitstream( /* Reserve child blocks for new created block */ bitstream_manager.reserve_child_blocks( grid_configurable_block, count_module_manager_module_configurable_children( - module_manager, grid_module, ModuleManager::e_config_child_type::PHYSICAL)); + module_manager, grid_module, + ModuleManager::e_config_child_type::PHYSICAL)); /* Create a dedicated block for the non-unified configurable child */ if (!module_manager.unified_configurable_children(grid_module)) { - VTR_ASSERT(1 == module_manager.configurable_children(grid_module, ModuleManager::e_config_child_type::PHYSICAL).size()); - std::string phy_mem_instance_name = module_manager.instance_name(grid_module, module_manager.configurable_children(grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); - ConfigBlockId grid_grouped_config_block = bitstream_manager.add_block(phy_mem_instance_name); - bitstream_manager.add_child_block(grid_configurable_block, grid_grouped_config_block); + VTR_ASSERT(1 == + module_manager + .configurable_children( + grid_module, ModuleManager::e_config_child_type::PHYSICAL) + .size()); + std::string phy_mem_instance_name = module_manager.instance_name( + grid_module, + module_manager.configurable_children( + grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0], + module_manager.configurable_child_instances( + grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); + ConfigBlockId grid_grouped_config_block = + bitstream_manager.add_block(phy_mem_instance_name); + bitstream_manager.add_child_block(grid_configurable_block, + grid_grouped_config_block); grid_configurable_block = grid_grouped_config_block; } diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index 32a07fe05..5716f8743 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -486,8 +486,9 @@ static void build_connection_block_bitstreams( VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); /* Bypass empty blocks which have none configurable children */ - if (0 == count_module_manager_module_configurable_children(module_manager, - cb_module, ModuleManager::e_config_child_type::LOGICAL)) { + if (0 == count_module_manager_module_configurable_children( + module_manager, cb_module, + ModuleManager::e_config_child_type::LOGICAL)) { continue; } @@ -527,15 +528,27 @@ static void build_connection_block_bitstreams( /* Reserve child blocks for new created block */ bitstream_manager.reserve_child_blocks( cb_configurable_block, - count_module_manager_module_configurable_children(module_manager, - cb_module, ModuleManager::e_config_child_type::PHYSICAL)); + count_module_manager_module_configurable_children( + module_manager, cb_module, + ModuleManager::e_config_child_type::PHYSICAL)); /* Create a dedicated block for the non-unified configurable child */ if (!module_manager.unified_configurable_children(cb_module)) { - VTR_ASSERT(1 == module_manager.configurable_children(cb_module, ModuleManager::e_config_child_type::PHYSICAL).size()); - std::string phy_mem_instance_name = module_manager.instance_name(cb_module, module_manager.configurable_children(cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); - ConfigBlockId cb_grouped_config_block = bitstream_manager.add_block(phy_mem_instance_name); - bitstream_manager.add_child_block(cb_configurable_block, cb_grouped_config_block); + VTR_ASSERT(1 == + module_manager + .configurable_children( + cb_module, ModuleManager::e_config_child_type::PHYSICAL) + .size()); + std::string phy_mem_instance_name = module_manager.instance_name( + cb_module, + module_manager.configurable_children( + cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], + module_manager.configurable_child_instances( + cb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); + ConfigBlockId cb_grouped_config_block = + bitstream_manager.add_block(phy_mem_instance_name); + bitstream_manager.add_child_block(cb_configurable_block, + cb_grouped_config_block); cb_configurable_block = cb_grouped_config_block; } @@ -602,8 +615,9 @@ void build_routing_bitstream( VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); /* Bypass empty blocks which have none configurable children */ - if (0 == count_module_manager_module_configurable_children(module_manager, - sb_module, ModuleManager::e_config_child_type::LOGICAL)) { + if (0 == count_module_manager_module_configurable_children( + module_manager, sb_module, + ModuleManager::e_config_child_type::LOGICAL)) { continue; } @@ -638,15 +652,27 @@ void build_routing_bitstream( /* Reserve child blocks for new created block */ bitstream_manager.reserve_child_blocks( sb_configurable_block, - count_module_manager_module_configurable_children(module_manager, - sb_module, ModuleManager::e_config_child_type::PHYSICAL)); + count_module_manager_module_configurable_children( + module_manager, sb_module, + ModuleManager::e_config_child_type::PHYSICAL)); /* Create a dedicated block for the non-unified configurable child */ if (!module_manager.unified_configurable_children(sb_module)) { - VTR_ASSERT(1 == module_manager.configurable_children(sb_module, ModuleManager::e_config_child_type::PHYSICAL).size()); - std::string phy_mem_instance_name = module_manager.instance_name(sb_module, module_manager.configurable_children(sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], module_manager.configurable_child_instances(sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); - ConfigBlockId sb_grouped_config_block = bitstream_manager.add_block(phy_mem_instance_name); - bitstream_manager.add_child_block(sb_configurable_block, sb_grouped_config_block); + VTR_ASSERT(1 == + module_manager + .configurable_children( + sb_module, ModuleManager::e_config_child_type::PHYSICAL) + .size()); + std::string phy_mem_instance_name = module_manager.instance_name( + sb_module, + module_manager.configurable_children( + sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0], + module_manager.configurable_child_instances( + sb_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); + ConfigBlockId sb_grouped_config_block = + bitstream_manager.add_block(phy_mem_instance_name); + bitstream_manager.add_child_block(sb_configurable_block, + sb_grouped_config_block); sb_configurable_block = sb_grouped_config_block; } diff --git a/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp index 480e4c915..9d6d21def 100644 --- a/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp @@ -49,13 +49,17 @@ static void rec_print_pnr_sdc_constrain_configurable_chain( ModuleId& previous_module) { /* For each configurable child, we will go one level down in priority */ for (size_t child_index = 0; - child_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + child_index < + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); ++child_index) { std::string child_module_path = parent_module_path; - ModuleId child_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; - size_t child_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; + ModuleId child_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; + size_t child_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; std::string child_instance_name; if (true == module_manager @@ -79,7 +83,10 @@ static void rec_print_pnr_sdc_constrain_configurable_chain( /* If there is no configurable children any more, this is a leaf module, print * a SDC command for disable timing */ - if (0 < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size()) { + if (0 < module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size()) { return; } diff --git a/openfpga/src/fpga_sdc/sdc_memory_utils.cpp b/openfpga/src/fpga_sdc/sdc_memory_utils.cpp index ef635bd37..7d8df9541 100644 --- a/openfpga/src/fpga_sdc/sdc_memory_utils.cpp +++ b/openfpga/src/fpga_sdc/sdc_memory_utils.cpp @@ -45,13 +45,17 @@ void rec_print_pnr_sdc_disable_configurable_memory_module_output( /* For each configurable child, we will go one level down in priority */ for (size_t child_index = 0; - child_index < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size(); + child_index < + module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); ++child_index) { std::string child_module_path = parent_module_path; - ModuleId child_module_id = - module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; - size_t child_instance_id = - module_manager.configurable_child_instances(parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; + ModuleId child_module_id = module_manager.configurable_children( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; + size_t child_instance_id = module_manager.configurable_child_instances( + parent_module, ModuleManager::e_config_child_type::PHYSICAL)[child_index]; std::string child_instance_name; if (true == module_manager @@ -96,7 +100,10 @@ void rec_print_pnr_sdc_disable_configurable_memory_module_output( /* If there is no configurable children any more, this is a leaf module, print * a SDC command for disable timing */ - if (0 < module_manager.configurable_children(parent_module, ModuleManager::e_config_child_type::PHYSICAL).size()) { + if (0 < module_manager + .configurable_children(parent_module, + ModuleManager::e_config_child_type::PHYSICAL) + .size()) { return; } diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index a759b1d9d..a26cc48a0 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -3,6 +3,7 @@ * generating ports for memory modules *********************************************************************/ #include "memory_utils.h" + #include "command_exit_codes.h" #include "decoder_library_utils.h" #include "openfpga_naming.h" @@ -344,7 +345,8 @@ std::vector generate_sram_port_names( case CONFIG_MEM_FEEDTHROUGH: /* Feed through wires are all inputs */ model_port_types.push_back(CIRCUIT_MODEL_PORT_BL); /* Indicate mem port */ - model_port_types.push_back(CIRCUIT_MODEL_PORT_BLB); /* Indicate mem_inv port */ + model_port_types.push_back( + CIRCUIT_MODEL_PORT_BLB); /* Indicate mem_inv port */ break; case CONFIG_MEM_SCAN_CHAIN: model_port_types.push_back(CIRCUIT_MODEL_PORT_INPUT); @@ -495,43 +497,81 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( return num_child_to_skip; } -int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector& physical_memory_children) { - if (module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).empty()) { +int rec_find_physical_memory_children( + const ModuleManager& module_manager, const ModuleId& curr_module, + std::vector& physical_memory_children) { + if (module_manager + .configurable_children(curr_module, + ModuleManager::e_config_child_type::LOGICAL) + .empty()) { return CMD_EXEC_SUCCESS; } - for (size_t ichild = 0; ichild < module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).size(); ++ichild) { - ModuleId logical_child = module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; - if (module_manager.configurable_children(logical_child, ModuleManager::e_config_child_type::LOGICAL).empty()) { + for (size_t ichild = 0; + ichild < module_manager + .configurable_children( + curr_module, ModuleManager::e_config_child_type::LOGICAL) + .size(); + ++ichild) { + ModuleId logical_child = module_manager.configurable_children( + curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; + if (module_manager + .configurable_children(logical_child, + ModuleManager::e_config_child_type::LOGICAL) + .empty()) { /* This is a leaf node, get the physical memory module */ - physical_memory_children.push_back(module_manager.logical2physical_configurable_children(curr_module)[ichild]); + physical_memory_children.push_back( + module_manager.logical2physical_configurable_children( + curr_module)[ichild]); } else { - rec_find_physical_memory_children(module_manager, logical_child, physical_memory_children); + rec_find_physical_memory_children(module_manager, logical_child, + physical_memory_children); } } - return CMD_EXEC_SUCCESS; + return CMD_EXEC_SUCCESS; } -int rec_update_logical_memory_children_with_physical_mapping(ModuleManager& module_manager, const ModuleId& curr_module, const ModuleId& phy_mem_module, std::map& logical_mem_child_inst_count) { - if (module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).empty()) { +int rec_update_logical_memory_children_with_physical_mapping( + ModuleManager& module_manager, const ModuleId& curr_module, + const ModuleId& phy_mem_module, + std::map& logical_mem_child_inst_count) { + if (module_manager + .configurable_children(curr_module, + ModuleManager::e_config_child_type::LOGICAL) + .empty()) { return CMD_EXEC_SUCCESS; } - for (size_t ichild = 0; ichild < module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL).size(); ++ichild) { - ModuleId logical_child = module_manager.configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; - if (module_manager.configurable_children(logical_child, ModuleManager::e_config_child_type::LOGICAL).empty()) { + for (size_t ichild = 0; + ichild < module_manager + .configurable_children( + curr_module, ModuleManager::e_config_child_type::LOGICAL) + .size(); + ++ichild) { + ModuleId logical_child = module_manager.configurable_children( + curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; + if (module_manager + .configurable_children(logical_child, + ModuleManager::e_config_child_type::LOGICAL) + .empty()) { /* This is a leaf node, update its physical information */ - ModuleId phy_mem_submodule = module_manager.logical2physical_configurable_children(curr_module)[ichild]; + ModuleId phy_mem_submodule = + module_manager.logical2physical_configurable_children( + curr_module)[ichild]; auto result = logical_mem_child_inst_count.find(phy_mem_submodule); if (result == logical_mem_child_inst_count.end()) { logical_mem_child_inst_count[phy_mem_submodule] = 0; } - module_manager.set_logical2physical_configurable_child_instance(curr_module, ichild, logical_mem_child_inst_count[phy_mem_submodule]); - module_manager.set_logical2physical_configurable_child_parent_module(curr_module, ichild, phy_mem_module); + module_manager.set_logical2physical_configurable_child_instance( + curr_module, ichild, logical_mem_child_inst_count[phy_mem_submodule]); + module_manager.set_logical2physical_configurable_child_parent_module( + curr_module, ichild, phy_mem_module); logical_mem_child_inst_count[phy_mem_submodule]++; } else { - rec_update_logical_memory_children_with_physical_mapping(module_manager, logical_child, phy_mem_module, logical_mem_child_inst_count); + rec_update_logical_memory_children_with_physical_mapping( + module_manager, logical_child, phy_mem_module, + logical_mem_child_inst_count); } } - return CMD_EXEC_SUCCESS; + return CMD_EXEC_SUCCESS; } } /* end namespace openfpga */ diff --git a/openfpga/src/utils/memory_utils.h b/openfpga/src/utils/memory_utils.h index 7714242fa..14243ec79 100644 --- a/openfpga/src/utils/memory_utils.h +++ b/openfpga/src/utils/memory_utils.h @@ -55,17 +55,25 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( /** * @brief Find the physical memory child modules with a given root module - * This function will walk through the module tree in a recursive way until reaching the leaf node (which require configurable memories) - * Return a list of modules + * This function will walk through the module tree in a recursive way until + * reaching the leaf node (which require configurable memories) Return a list of + * modules */ -int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector& physical_memory_children); +int rec_find_physical_memory_children( + const ModuleManager& module_manager, const ModuleId& curr_module, + std::vector& physical_memory_children); /** - * @brief Update all the mappings between logical-to-physical memory children with a given root module - * This function will walk through the module tree in a recursive way until reaching the leaf node (which require configurable memories) - * Keep a scoreboard of instance number for checking. Note that when calling this the function, use an empty scoreboard! + * @brief Update all the mappings between logical-to-physical memory children + * with a given root module This function will walk through the module tree in a + * recursive way until reaching the leaf node (which require configurable + * memories) Keep a scoreboard of instance number for checking. Note that when + * calling this the function, use an empty scoreboard! */ -int rec_update_logical_memory_children_with_physical_mapping(ModuleManager& module_manager, const ModuleId& curr_module, const ModuleId& phy_mem_module, std::map& logical_mem_child_inst_count); +int rec_update_logical_memory_children_with_physical_mapping( + ModuleManager& module_manager, const ModuleId& curr_module, + const ModuleId& phy_mem_module, + std::map& logical_mem_child_inst_count); } /* end namespace openfpga */ diff --git a/openfpga/src/utils/module_manager_memory_utils.cpp b/openfpga/src/utils/module_manager_memory_utils.cpp index 6cfcf2dbb..b7170f833 100644 --- a/openfpga/src/utils/module_manager_memory_utils.cpp +++ b/openfpga/src/utils/module_manager_memory_utils.cpp @@ -39,7 +39,10 @@ static bool submodule_memory_modules_match_fabric_key( const FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { /* If the length does not match, conclusion is easy to be made */ size_t len_module_memory = - module_manager.configurable_children(module_id, ModuleManager::e_config_child_type::PHYSICAL).size(); + module_manager + .configurable_children(module_id, + ModuleManager::e_config_child_type::PHYSICAL) + .size(); size_t len_fabric_sub_key = fabric_key.sub_keys(key_module_id).size(); if (len_module_memory != len_fabric_sub_key) { return false; @@ -65,9 +68,11 @@ static bool submodule_memory_modules_match_fabric_key( inst_info.second = fabric_key.sub_key_value(key_id); } if (inst_info.first != - module_manager.configurable_children(module_id, ModuleManager::e_config_child_type::PHYSICAL)[ikey] || + module_manager.configurable_children( + module_id, ModuleManager::e_config_child_type::PHYSICAL)[ikey] || inst_info.second != - module_manager.configurable_child_instances(module_id, ModuleManager::e_config_child_type::PHYSICAL)[ikey]) { + module_manager.configurable_child_instances( + module_id, ModuleManager::e_config_child_type::PHYSICAL)[ikey]) { return false; } } @@ -143,7 +148,8 @@ static bool update_submodule_memory_modules_from_fabric_key( /* Now we can add the child to configurable children of the top module */ module_manager.add_configurable_child(module_id, inst_info.first, - inst_info.second, config_child_type, vtr::Point()); + inst_info.second, config_child_type, + vtr::Point()); } return CMD_EXEC_SUCCESS; } @@ -156,7 +162,9 @@ static int remove_submodule_nets_cmos_memory_chain_config_bus( const e_config_protocol_type& sram_orgz_type, const ModuleManager::e_config_child_type& config_child_type) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); + mem_index < + module_manager.configurable_children(parent_module, config_child_type) + .size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -173,8 +181,8 @@ static int remove_submodule_nets_cmos_memory_chain_config_bus( } else { /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); - net_src_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index - 1]; + net_src_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( parent_module, config_child_type)[mem_index - 1]; net_src_port_id = @@ -203,9 +211,12 @@ static int remove_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module, config_child_type).back(); + module_manager.configurable_children(parent_module, config_child_type) + .back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type).back(); + module_manager + .configurable_child_instances(parent_module, config_child_type) + .back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -304,7 +315,9 @@ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( const e_config_protocol_type& sram_orgz_type, const ModuleManager::e_config_child_type& config_child_type) { for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); + mem_index < + module_manager.configurable_children(parent_module, config_child_type) + .size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -325,17 +338,17 @@ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); - net_sink_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); - net_src_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index - 1]; + net_src_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( parent_module, config_child_type)[mem_index - 1]; net_src_port_id = @@ -343,10 +356,10 @@ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); - net_sink_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -380,9 +393,12 @@ static int rebuild_submodule_nets_cmos_memory_chain_config_bus( /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module, config_child_type).back(); + module_manager.configurable_children(parent_module, config_child_type) + .back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type).back(); + module_manager + .configurable_child_instances(parent_module, config_child_type) + .back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -509,20 +525,22 @@ static int load_and_update_submodule_memory_modules_from_fabric_key( } /* Do not match, now remove all the nets for the configurable children */ status = remove_submodule_configurable_children_nets( - module_manager, module_id, circuit_lib, config_protocol, ModuleManager::e_config_child_type::PHYSICAL); + module_manager, module_id, circuit_lib, config_protocol, + ModuleManager::e_config_child_type::PHYSICAL); if (status == CMD_EXEC_FATAL_ERROR) { return status; } /* Overwrite the configurable children list */ status = update_submodule_memory_modules_from_fabric_key( - module_manager, module_id, circuit_lib, config_protocol, ModuleManager::e_config_child_type::PHYSICAL, fabric_key, - key_module_id); + module_manager, module_id, circuit_lib, config_protocol, + ModuleManager::e_config_child_type::PHYSICAL, fabric_key, key_module_id); if (status == CMD_EXEC_FATAL_ERROR) { return status; } /* TODO: Create the nets for the new list of configurable children */ status = rebuild_submodule_configurable_children_nets( - module_manager, module_id, circuit_lib, config_protocol, ModuleManager::e_config_child_type::PHYSICAL); + module_manager, module_id, circuit_lib, config_protocol, + ModuleManager::e_config_child_type::PHYSICAL); if (status == CMD_EXEC_FATAL_ERROR) { return status; } diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 8360aea3b..7d0db1659 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -86,11 +86,14 @@ void reserve_module_manager_module_nets(ModuleManager& module_manager, *children as well ******************************************************************************/ size_t count_module_manager_module_configurable_children( - const ModuleManager& module_manager, const ModuleId& module, const ModuleManager::e_config_child_type& config_child_type) { + const ModuleManager& module_manager, const ModuleId& module, + const ModuleManager::e_config_child_type& config_child_type) { size_t num_config_children = 0; - for (const ModuleId& child : module_manager.configurable_children(module, config_child_type)) { - if (0 != module_manager.configurable_children(child, config_child_type).size()) { + for (const ModuleId& child : + module_manager.configurable_children(module, config_child_type)) { + if (0 != + module_manager.configurable_children(child, config_child_type).size()) { num_config_children++; } } @@ -1041,7 +1044,8 @@ void add_module_nets_cmos_flatten_memory_config_bus( module_manager.module_port(net_src_module_id, net_src_port_id); for (size_t mem_index = 0; - mem_index < module_manager.num_configurable_children(parent_module, config_child_type); + mem_index < module_manager.num_configurable_children(parent_module, + config_child_type); ++mem_index) { ModuleId net_sink_module_id; size_t net_sink_instance_id; @@ -1050,10 +1054,10 @@ void add_module_nets_cmos_flatten_memory_config_bus( /* Find the port name of next memory module */ std::string sink_port_name = generate_sram_port_name(sram_orgz_type, config_port_type); - net_sink_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); @@ -1125,15 +1129,17 @@ void add_module_nets_cmos_memory_bank_bl_config_bus( module_manager.module_port(net_src_module_id, net_src_port_id); for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); + mem_index < + module_manager.configurable_children(parent_module, config_child_type) + .size(); ++mem_index) { /* Find the port name of next memory module */ std::string sink_port_name = generate_sram_port_name(sram_orgz_type, config_port_type); - ModuleId net_sink_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index]; - size_t net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; + ModuleId net_sink_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index]; + size_t net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; ModulePortId net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); @@ -1221,15 +1227,17 @@ void add_module_nets_cmos_memory_bank_wl_config_bus( module_manager.module_port(net_src_module_id, net_bl_port_id); for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module, config_child_type).size(); + mem_index < + module_manager.configurable_children(parent_module, config_child_type) + .size(); ++mem_index) { /* Find the port name of next memory module */ std::string sink_port_name = generate_sram_port_name(sram_orgz_type, config_port_type); - ModuleId net_sink_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index]; - size_t net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; + ModuleId net_sink_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index]; + size_t net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; ModulePortId net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); @@ -1279,7 +1287,8 @@ void add_module_nets_cmos_memory_chain_config_bus( const e_config_protocol_type& sram_orgz_type, const ModuleManager::e_config_child_type& config_child_type) { for (size_t mem_index = 0; - mem_index < module_manager.num_configurable_children(parent_module, config_child_type); + mem_index < module_manager.num_configurable_children(parent_module, + config_child_type); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -1300,17 +1309,17 @@ void add_module_nets_cmos_memory_chain_config_bus( /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); - net_sink_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } else { /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); - net_src_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index - 1]; + net_src_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index - 1]; net_src_instance_id = module_manager.configurable_child_instances( parent_module, config_child_type)[mem_index - 1]; net_src_port_id = @@ -1318,10 +1327,10 @@ void add_module_nets_cmos_memory_chain_config_bus( /* Find the port name of next memory module */ std::string sink_port_name = generate_configuration_chain_head_name(); - net_sink_module_id = - module_manager.configurable_children(parent_module, config_child_type)[mem_index]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type)[mem_index]; + net_sink_module_id = module_manager.configurable_children( + parent_module, config_child_type)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); } @@ -1355,9 +1364,12 @@ void add_module_nets_cmos_memory_chain_config_bus( /* Find the port name of previous memory module */ std::string src_port_name = generate_configuration_chain_tail_name(); ModuleId net_src_module_id = - module_manager.configurable_children(parent_module, config_child_type).back(); + module_manager.configurable_children(parent_module, config_child_type) + .back(); size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module, config_child_type).back(); + module_manager + .configurable_child_instances(parent_module, config_child_type) + .back(); ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); @@ -1577,8 +1589,8 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( 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, config_child_type)[mem_index]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; ModulePortId child_addr_port = module_manager.find_module_port( child_module, std::string(DECODER_ADDRESS_PORT_NAME)); BasicPort child_addr_port_info = @@ -1609,8 +1621,8 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( 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, config_child_type)[mem_index]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; ModulePortId child_din_port = module_manager.find_module_port( child_module, std::string(DECODER_DATA_IN_PORT_NAME)); add_module_bus_nets(module_manager, parent_module, parent_module, 0, @@ -1630,8 +1642,8 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( 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, config_child_type)[mem_index]; + size_t child_instance = module_manager.configurable_child_instances( + parent_module, config_child_type)[mem_index]; ModulePortId child_en_port = module_manager.find_module_port( child_module, std::string(DECODER_ENABLE_PORT_NAME)); BasicPort child_en_port_info = @@ -1655,7 +1667,8 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( } /* Add the decoder as the last configurable children */ - module_manager.add_configurable_child(parent_module, decoder_module, 0, config_child_type); + module_manager.add_configurable_child(parent_module, decoder_module, 0, + config_child_type); } /********************************************************************* @@ -1671,15 +1684,18 @@ void add_module_nets_cmos_memory_frame_config_bus( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const ModuleId& parent_module, const ModuleManager::e_config_child_type& config_child_type) { - if (0 == module_manager.num_configurable_children(parent_module, config_child_type)) { + if (0 == module_manager.num_configurable_children(parent_module, + config_child_type)) { return; } - if (1 == module_manager.num_configurable_children(parent_module, config_child_type)) { - add_module_nets_cmos_memory_frame_short_config_bus(module_manager, - parent_module, config_child_type); + if (1 == module_manager.num_configurable_children(parent_module, + config_child_type)) { + add_module_nets_cmos_memory_frame_short_config_bus( + module_manager, parent_module, config_child_type); } else { - VTR_ASSERT(1 < module_manager.num_configurable_children(parent_module, config_child_type)); + VTR_ASSERT(1 < module_manager.num_configurable_children(parent_module, + config_child_type)); add_module_nets_cmos_memory_frame_decoder_config_bus( module_manager, decoder_lib, parent_module, config_child_type); } @@ -1741,23 +1757,28 @@ static void add_module_nets_cmos_memory_config_bus( } case CONFIG_MEM_FEEDTHROUGH: add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, + config_child_type); add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BLB, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BLB, + config_child_type); break; case CONFIG_MEM_STANDALONE: case CONFIG_MEM_QL_MEMORY_BANK: case CONFIG_MEM_MEMORY_BANK: add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, + config_child_type); add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, + config_child_type); add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR, + config_child_type); break; case CONFIG_MEM_FRAME_BASED: - add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, - parent_module, config_child_type); + add_module_nets_cmos_memory_frame_config_bus( + module_manager, decoder_lib, parent_module, config_child_type); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, @@ -1814,21 +1835,26 @@ static void add_pb_module_nets_cmos_memory_config_bus( case CONFIG_MEM_STANDALONE: case CONFIG_MEM_QL_MEMORY_BANK: add_module_nets_cmos_memory_bank_bl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, + config_child_type); add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, + config_child_type); add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR, + config_child_type); break; case CONFIG_MEM_MEMORY_BANK: add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL, + config_child_type); add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, config_child_type); + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL, + config_child_type); break; case CONFIG_MEM_FRAME_BASED: - add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, - parent_module, config_child_type); + add_module_nets_cmos_memory_frame_config_bus( + module_manager, decoder_lib, parent_module, config_child_type); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, @@ -1899,7 +1925,8 @@ void add_module_nets_memory_config_bus( switch (mem_tech) { case CIRCUIT_MODEL_DESIGN_CMOS: add_module_nets_cmos_memory_config_bus(module_manager, decoder_lib, - parent_module, sram_orgz_type, config_child_type); + parent_module, sram_orgz_type, + config_child_type); break; case CIRCUIT_MODEL_DESIGN_RRAM: /* TODO: */ @@ -1931,7 +1958,8 @@ void add_pb_module_nets_memory_config_bus( switch (mem_tech) { case CIRCUIT_MODEL_DESIGN_CMOS: add_pb_module_nets_cmos_memory_config_bus(module_manager, decoder_lib, - parent_module, sram_orgz_type, config_child_type); + parent_module, sram_orgz_type, + config_child_type); break; case CIRCUIT_MODEL_DESIGN_RRAM: /* TODO: */ @@ -2420,9 +2448,11 @@ size_t find_module_num_config_bits_from_child_modules( /* If there are more than 2 configurable children, we need a decoder * Otherwise, we can just short wire the address port to the children */ - if (1 < module_manager.num_configurable_children(module_id, config_child_type)) { + if (1 < module_manager.num_configurable_children(module_id, + config_child_type)) { num_config_bits += find_mux_local_decoder_addr_size( - module_manager.num_configurable_children(module_id, config_child_type)); + module_manager.num_configurable_children(module_id, + config_child_type)); } break; diff --git a/openfpga/src/utils/module_manager_utils.h b/openfpga/src/utils/module_manager_utils.h index ca3a1ac30..f55f73402 100644 --- a/openfpga/src/utils/module_manager_utils.h +++ b/openfpga/src/utils/module_manager_utils.h @@ -42,7 +42,8 @@ void reserve_module_manager_module_nets(ModuleManager& module_manager, const ModuleId& module); size_t count_module_manager_module_configurable_children( - const ModuleManager& module_manager, const ModuleId& module, const ModuleManager::e_config_child_type& config_child_type); + const ModuleManager& module_manager, const ModuleId& module, + const ModuleManager::e_config_child_type& config_child_type); std::pair find_module_manager_instance_module_info( const ModuleManager& module_manager, const ModuleId& parent, From 2aeeb0cacf2f8222bb1005fb6b8e817b80bc0615 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 22:13:27 -0700 Subject: [PATCH 15/41] [core] fixed a bug which causes reg tests failed --- openfpga/src/fabric/module_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index e71378ce1..2e8ca26cc 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -420,7 +420,7 @@ size_t ModuleManager::num_configurable_children( if (type == ModuleManager::e_config_child_type::LOGICAL) { return logical_configurable_children_[parent_module].size(); } - VTR_ASSERT(type == ModuleManager::e_config_child_type::LOGICAL); + VTR_ASSERT(type == ModuleManager::e_config_child_type::PHYSICAL); return physical_configurable_children_[parent_module].size(); } From b7048d3dc8ff518c37fda63ca9feca6f95367e74 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 22:30:41 -0700 Subject: [PATCH 16/41] [test] adding new tests to validate group config block --- ...ock_full_testbench_example_script.openfpga | 72 +++++++++++++++++++ .../regression_test_scripts/basic_reg_test.sh | 4 ++ .../config/task.conf | 35 +++++++++ .../config/tile_config.xml | 1 + .../config/task.conf | 35 +++++++++ 5 files changed, 147 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/tile_config.xml create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf diff --git a/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga new file mode 100644 index 000000000..244674cd4 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga @@ -0,0 +1,72 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route + +# Read OpenFPGA architecture definition +read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} + +# Read OpenFPGA simulation settings +read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE} + +# Annotate the OpenFPGA architecture to VPR data base +# to debug use --verbose options +link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges + +# Check and correct any naming conflicts in the BLIF netlist +check_netlist_naming_conflict --fix --report ./netlist_renaming.xml + +# Apply fix-up to Look-Up Table truth tables based on packing results +lut_truth_table_fixup + +# Build the module graph +# - Enabled compression on routing architecture modules +# - Enable pin duplication on grid modules +build_fabric --compress_routing --group_config_block ${OPENFPGA_GROUP_TILE_CONFIG_OPTION} #--verbose + +# Write the fabric hierarchy of module graph to a file +# This is used by hierarchical PnR flows +write_fabric_hierarchy --file ./fabric_hierarchy.txt + +# Repack the netlist to physical pbs +# This must be done before bitstream generator and testbench generation +# Strongly recommend it is done after all the fix-up have been applied +repack #--verbose + +# Build the bitstream +# - Output the fabric-independent bitstream to a file +build_architecture_bitstream --verbose --write_file fabric_independent_bitstream.xml + +# Build fabric-dependent bitstream +build_fabric_bitstream --verbose + +# Write fabric-dependent bitstream +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text + +# Write the Verilog netlist for FPGA fabric +# - Enable the use of explicit port mapping in Verilog netlist +write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose + +# Write the Verilog testbench for FPGA fabric +# - We suggest the use of same output directory as fabric Verilog netlists +# - Must specify the reference benchmark file if you want to output any testbenches +# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA +# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase +# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit + +# Write the SDC files for PnR backend +# - Turn on every options here +# FIXME: Not supported yet. +#write_pnr_sdc --file ./SDC + +# Write SDC to disable timing for configure ports +write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc + +# Write the SDC to run timing analysis for a mapped FPGA fabric +write_analysis_sdc --file ./SDC_analysis + +# Finish and exit OpenFPGA +exit + +# Note : +# To run verification at the end of the flow maintain source in ./SRC directory diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index ed9877c80..f88ceccc0 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -186,6 +186,10 @@ run-task basic_tests/tile_organization/homo_fabric_tile_adder_chain $@ run-task basic_tests/tile_organization/homo_fabric_tile_clkntwk $@ run-task basic_tests/tile_organization/hetero_fabric_tile $@ +echo -e "Testing group config block"; +run-task basic_tests/group_config_block/group_config_block_homo_full_testbench $@ +run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile $@ + echo -e "Testing global port definition from tiles"; run-task basic_tests/global_tile_ports/global_tile_clock $@ run-task basic_tests/global_tile_ports/global_tile_reset $@ diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf new file mode 100644 index 000000000..94e540f67 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf @@ -0,0 +1,35 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Configuration file for running experiments +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs +# Each job execute fpga_flow script on combination of architecture & benchmark +# timeout_each_job is timeout for each job +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +[GENERAL] +run_engine=openfpga_shell +power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml +power_analysis = true +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_group_tile_config_option=--group_tile ${PATH:TASK_DIR}/config/tile_config.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = or2 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/tile_config.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/tile_config.xml @@ -0,0 +1 @@ + diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf new file mode 100644 index 000000000..f4273fdbf --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf @@ -0,0 +1,35 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Configuration file for running experiments +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs +# Each job execute fpga_flow script on combination of architecture & benchmark +# timeout_each_job is timeout for each job +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +[GENERAL] +run_engine=openfpga_shell +power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml +power_analysis = true +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_group_tile_config_option= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = or2 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= From 99bda2e5b0839f7cef0c2cf055b0126fc8fc146a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Aug 2023 22:50:14 -0700 Subject: [PATCH 17/41] [core] debugging --- openfpga/src/fabric/build_grid_modules.cpp | 7 ++++++- openfpga/src/utils/circuit_library_utils.cpp | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 97ce3e8bd..b96a3acab 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -278,7 +278,8 @@ static void build_primitive_block_module( std::string primitive_module_name = generate_physical_block_module_name(primitive_pb_graph_node->pb_type); - VTR_LOGV(verbose, "Building module '%s'...", primitive_module_name.c_str()); + VTR_LOGV(verbose, "Building primitive module '%s'...", + primitive_module_name.c_str()); /* Create a module of the primitive LUT and register it to module manager */ ModuleId primitive_module = module_manager.add_module(primitive_module_name); @@ -1036,6 +1037,9 @@ static void rec_build_logical_tile_modules( module_manager.set_child_instance_name( pb_module, child_pb_module, child_instance_id, child_pb_instance_name); + VTR_LOGV(verbose, "Building instance '%s'\n", + child_pb_instance_name.c_str()); + /* Identify if this sub module includes configuration bits, * we will update the memory module and instance list */ @@ -1053,6 +1057,7 @@ static void rec_build_logical_tile_modules( /* Add modules and nets for programmable/non-programmable interconnections * inside the Verilog module */ + VTR_LOGV(verbose, "Building local interconnecting modules\n"); add_module_pb_graph_interc(module_manager, pb_module, memory_modules, memory_instances, device_annotation, circuit_lib, physical_pb_graph_node, physical_mode->index, diff --git a/openfpga/src/utils/circuit_library_utils.cpp b/openfpga/src/utils/circuit_library_utils.cpp index abcac433c..729050e06 100644 --- a/openfpga/src/utils/circuit_library_utils.cpp +++ b/openfpga/src/utils/circuit_library_utils.cpp @@ -188,6 +188,7 @@ size_t find_circuit_num_config_bits( } switch (config_protocol_type) { + case CONFIG_MEM_FEEDTHROUGH: case CONFIG_MEM_STANDALONE: case CONFIG_MEM_SCAN_CHAIN: case CONFIG_MEM_QL_MEMORY_BANK: From 3c2518ac7073dc78dcba26c454884c6b0764b2de Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Aug 2023 11:20:05 -0700 Subject: [PATCH 18/41] [core] adding debugging message when verbose is enabled --- openfpga/src/fabric/build_device_module.cpp | 2 +- openfpga/src/fabric/build_memory_modules.cpp | 53 +++++++++++++------- openfpga/src/fabric/build_memory_modules.h | 3 +- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 5a833c88a..fbe3d85eb 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -79,7 +79,7 @@ int build_device_module_graph( build_memory_modules(module_manager, decoder_lib, openfpga_ctx.mux_lib(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.arch().config_protocol.type(), - group_config_block); + group_config_block, verbose); /* Build grid and programmable block modules */ build_grid_modules(module_manager, decoder_lib, vpr_device_ctx, diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index e9d7b9ae3..6300cf11e 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -424,7 +424,8 @@ static void build_memory_flatten_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const std::string& module_name, const CircuitModelId& sram_model, - const size_t& num_mems) { + const size_t& num_mems, + const bool& verbose) { /* Get the global ports required by the SRAM */ std::vector global_port_types; global_port_types.push_back(CIRCUIT_MODEL_PORT_CLOCK); @@ -453,6 +454,7 @@ static void build_memory_flatten_module(ModuleManager& module_manager, VTR_ASSERT(2 == sram_output_ports.size()); /* Create a module and add to the module manager */ + VTR_LOGV(verbose, "Building memory module '%s'\n", module_name.c_str()); ModuleId mem_module = module_manager.add_module(module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); @@ -551,7 +553,8 @@ static void build_memory_chain_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const std::string& module_name, const CircuitModelId& sram_model, - const size_t& num_mems) { + const size_t& num_mems, + const bool& verbose) { /* Get the input ports from the SRAM */ std::vector sram_input_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_INPUT, true); @@ -567,6 +570,7 @@ static void build_memory_chain_module(ModuleManager& module_manager, (3 == sram_output_ports.size())); /* Create a module and add to the module manager */ + VTR_LOGV(verbose, "Building memory module '%s'\n", module_name.c_str()); ModuleId mem_module = module_manager.add_module(module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); @@ -711,7 +715,8 @@ static void build_frame_memory_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const std::string& module_name, const CircuitModelId& sram_model, - const size_t& num_mems) { + const size_t& num_mems, + const bool& verbose) { /* Get the global ports required by the SRAM */ std::vector global_port_types; global_port_types.push_back(CIRCUIT_MODEL_PORT_CLOCK); @@ -754,6 +759,7 @@ static void build_frame_memory_module(ModuleManager& module_manager, VTR_ASSERT(0 == sram_blb_ports.size()); /* Create a module and add to the module manager */ + VTR_LOGV(verbose, "Building memory module '%s'\n", module_name.c_str()); ModuleId mem_module = module_manager.add_module(module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); @@ -933,21 +939,21 @@ static void build_memory_module(ModuleManager& module_manager, const e_config_protocol_type& sram_orgz_type, const std::string& module_name, const CircuitModelId& sram_model, - const size_t& num_mems) { + const size_t& num_mems, const bool& verbose) { switch (sram_orgz_type) { case CONFIG_MEM_STANDALONE: case CONFIG_MEM_QL_MEMORY_BANK: case CONFIG_MEM_MEMORY_BANK: build_memory_flatten_module(module_manager, circuit_lib, module_name, - sram_model, num_mems); + sram_model, num_mems, verbose); break; case CONFIG_MEM_SCAN_CHAIN: build_memory_chain_module(module_manager, circuit_lib, module_name, - sram_model, num_mems); + sram_model, num_mems, verbose); break; case CONFIG_MEM_FRAME_BASED: build_frame_memory_module(module_manager, arch_decoder_lib, circuit_lib, - module_name, sram_model, num_mems); + module_name, sram_model, num_mems, verbose); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, @@ -976,8 +982,11 @@ static void build_memory_module(ModuleManager& module_manager, ********************************************************************/ static int build_feedthrough_memory_module(ModuleManager& module_manager, const std::string& module_name, - const size_t& num_mems) { + const size_t& num_mems, + const bool& verbose) { /* Create a module and add to the module manager */ + VTR_LOGV(verbose, "Building feedthrough memory module '%s'\n", + module_name.c_str()); ModuleId mem_module = module_manager.add_module(module_name); if (!module_manager.valid_module_id(mem_module)) { return CMD_EXEC_FATAL_ERROR; @@ -1048,7 +1057,7 @@ static void build_mux_memory_module( ModuleManager& module_manager, DecoderLibrary& arch_decoder_lib, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& mux_model, - const MuxGraph& mux_graph) { + const MuxGraph& mux_graph, const bool& verbose) { /* Find the actual number of configuration bits, based on the mux graph * Due to the use of local decoders inside mux, this may be */ @@ -1072,7 +1081,7 @@ static void build_mux_memory_module( build_memory_module(module_manager, arch_decoder_lib, circuit_lib, sram_orgz_type, module_name, sram_models[0], - num_config_bits); + num_config_bits, verbose); break; } case CIRCUIT_MODEL_DESIGN_RRAM: @@ -1107,7 +1116,7 @@ static void build_mux_memory_module( static int build_mux_feedthrough_memory_module( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, const CircuitModelId& mux_model, - const MuxGraph& mux_graph) { + const MuxGraph& mux_graph, const bool& verbose) { int status = CMD_EXEC_SUCCESS; /* Find the actual number of configuration bits, based on the mux graph * Due to the use of local decoders inside mux, this may be @@ -1126,7 +1135,7 @@ static int build_mux_feedthrough_memory_module( std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); status = build_feedthrough_memory_module(module_manager, module_name, - num_config_bits); + num_config_bits, verbose); break; } case CIRCUIT_MODEL_DESIGN_RRAM: @@ -1167,7 +1176,8 @@ int build_memory_modules(ModuleManager& module_manager, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const bool& require_feedthrough_memory) { + const bool& require_feedthrough_memory, + const bool& verbose) { int status = CMD_EXEC_SUCCESS; vtr::ScopedStartFinishTimer timer("Build memory modules"); @@ -1184,11 +1194,12 @@ int build_memory_modules(ModuleManager& module_manager, } /* Create a Verilog module for the memories used by the multiplexer */ build_mux_memory_module(module_manager, arch_decoder_lib, circuit_lib, - sram_orgz_type, mux_model, mux_graph); + sram_orgz_type, mux_model, mux_graph, verbose); /* Create feedthrough memory module */ if (require_feedthrough_memory) { - status = build_mux_feedthrough_memory_module( - module_manager, circuit_lib, sram_orgz_type, mux_model, mux_graph); + status = build_mux_feedthrough_memory_module(module_manager, circuit_lib, + sram_orgz_type, mux_model, + mux_graph, verbose); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -1228,11 +1239,15 @@ int build_memory_modules(ModuleManager& module_manager, /* Create a Verilog module for the memories used by the circuit model */ build_memory_module(module_manager, arch_decoder_lib, circuit_lib, - sram_orgz_type, module_name, sram_models[0], num_mems); + sram_orgz_type, module_name, sram_models[0], num_mems, + verbose); /* Create feedthrough memory module */ if (require_feedthrough_memory) { - status = - build_feedthrough_memory_module(module_manager, module_name, num_mems); + module_name = generate_memory_module_name( + circuit_lib, model, sram_models[0], + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + status = build_feedthrough_memory_module(module_manager, module_name, + num_mems, verbose); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index cc9c03bb2..ccd0ad240 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -27,7 +27,8 @@ int build_memory_modules(ModuleManager& module_manager, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const bool& require_feedthrough_memory); + const bool& require_feedthrough_memory, + const bool& verbose); int build_memory_group_module(ModuleManager& module_manager, DecoderLibrary& decoder_lib, From 5bc8925c3a6456c1f42465d9851e3c160ff3ef0b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Aug 2023 12:36:59 -0700 Subject: [PATCH 19/41] [core] fixed multiple bugs on fabric generator on supporting group_config_block --- openfpga/src/fabric/build_grid_modules.cpp | 20 ++++++++++++++----- openfpga/src/fabric/build_memory_modules.cpp | 20 ++++++++++++------- openfpga/src/fabric/build_memory_modules.h | 5 +++-- openfpga/src/fabric/build_routing_modules.cpp | 6 ++++-- openfpga/src/utils/memory_utils.cpp | 10 ++++++++-- openfpga/src/utils/memory_utils.h | 2 +- 6 files changed, 44 insertions(+), 19 deletions(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index b96a3acab..fa8c6a94c 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -278,7 +278,7 @@ static void build_primitive_block_module( std::string primitive_module_name = generate_physical_block_module_name(primitive_pb_graph_node->pb_type); - VTR_LOGV(verbose, "Building primitive module '%s'...", + VTR_LOGV(verbose, "Building primitive module '%s'...\n", primitive_module_name.c_str()); /* Create a module of the primitive LUT and register it to module manager */ @@ -372,6 +372,11 @@ static void build_primitive_block_module( std::string(MEMORY_MODULE_POSTFIX), false); ModuleId physical_memory_module = module_manager.find_module(physical_memory_module_name); + VTR_LOGV(verbose, + "Mapping feedthrough memory module '%s' to physical memory " + "module '%s'...\n", + memory_module_name.c_str(), physical_memory_module_name.c_str()); + VTR_ASSERT(module_manager.valid_module_id(physical_memory_module)); module_manager.set_logical2physical_configurable_child( primitive_module, config_child_id, physical_memory_module); } @@ -384,7 +389,7 @@ static void build_primitive_block_module( if (0 < module_manager.num_configurable_children( primitive_module, ModuleManager::e_config_child_type::LOGICAL)) { add_module_nets_memory_config_bus( - module_manager, decoder_lib, primitive_module, sram_orgz_type, + module_manager, decoder_lib, primitive_module, mem_module_type, circuit_lib.design_tech_type(sram_model), ModuleManager::e_config_child_type::LOGICAL); } @@ -680,7 +685,11 @@ static void add_module_pb_graph_pin_interc( std::string(MEMORY_MODULE_POSTFIX)); ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(phy_mem_module)); + VTR_ASSERT(module_manager.valid_module_id(phy_mem_module)); + VTR_LOGV(verbose, + "Mapping feedthrough memory module '%s' to physical memory " + "module '%s'...\n", + mux_mem_module_name.c_str(), phy_mem_module_name.c_str()); module_manager.set_logical2physical_configurable_child( pb_module, config_child_id, phy_mem_module); VTR_LOGV(verbose, "Now use a feedthrough memory for '%s'\n", @@ -978,7 +987,7 @@ static void rec_build_logical_tile_modules( std::string pb_module_name = generate_physical_block_module_name(physical_pb_type); - VTR_LOGV(verbose, "Building module '%s'...", pb_module_name.c_str()); + VTR_LOGV(verbose, "Building module '%s'...\n", pb_module_name.c_str()); /* Register the Verilog module in module manager */ ModuleId pb_module = module_manager.add_module(pb_module_name); @@ -1201,7 +1210,8 @@ static void build_physical_tile_module( /* TODO: Add a physical memory block */ if (group_config_block) { add_physical_memory_module(module_manager, decoder_lib, grid_module, - circuit_lib, sram_orgz_type, sram_model); + circuit_lib, sram_orgz_type, sram_model, + verbose); } /* Add grid ports(pins) to the module */ diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 6300cf11e..35e272d55 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1243,9 +1243,9 @@ int build_memory_modules(ModuleManager& module_manager, verbose); /* Create feedthrough memory module */ if (require_feedthrough_memory) { - module_name = generate_memory_module_name( - circuit_lib, model, sram_models[0], - std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + module_name = + generate_memory_module_name(circuit_lib, model, sram_models[0], + std::string(MEMORY_MODULE_POSTFIX), true); status = build_feedthrough_memory_module(module_manager, module_name, num_mems, verbose); if (status != CMD_EXEC_SUCCESS) { @@ -1310,7 +1310,9 @@ int build_memory_group_module(ModuleManager& module_manager, const std::string& module_name, const CircuitModelId& sram_model, const std::vector& child_modules, - const size_t& num_mems) { + const size_t& num_mems, const bool& verbose) { + VTR_LOGV(verbose, "Building memory group module '%s'...\n", + module_name.c_str()); ModuleId mem_module = module_manager.add_module(module_name); if (!module_manager.valid_module_id(mem_module)) { return CMD_EXEC_FATAL_ERROR; @@ -1442,13 +1444,14 @@ int add_physical_memory_module(ModuleManager& module_manager, const ModuleId& curr_module, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model) { + const CircuitModelId& sram_model, + const bool& verbose) { int status = CMD_EXEC_SUCCESS; std::vector required_phy_mem_modules; status = rec_find_physical_memory_children( static_cast(module_manager), curr_module, - required_phy_mem_modules); + required_phy_mem_modules, verbose); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -1463,12 +1466,15 @@ int add_physical_memory_module(ModuleManager& module_manager, } std::string phy_mem_module_name = generate_physical_memory_module_name( module_manager.module_name(curr_module), module_num_config_bits); + VTR_LOGV(verbose, "Adding memory group module '%s' as a child to '%s'...\n", + phy_mem_module_name.c_str(), + module_manager.module_name(curr_module).c_str()); ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); if (!module_manager.valid_module_id(phy_mem_module)) { status = build_memory_group_module(module_manager, decoder_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules, - module_num_config_bits); + module_num_config_bits, verbose); } if (status != CMD_EXEC_SUCCESS) { VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index ccd0ad240..bd04ff62f 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -37,14 +37,15 @@ int build_memory_group_module(ModuleManager& module_manager, const std::string& module_name, const CircuitModelId& sram_model, const std::vector& child_modules, - const size_t& num_mems); + const size_t& num_mems, const bool& verbose); int add_physical_memory_module(ModuleManager& module_manager, DecoderLibrary& decoder_lib, const ModuleId& curr_module, const CircuitLibrary& circuit_lib, const e_config_protocol_type& sram_orgz_type, - const CircuitModelId& sram_model); + const CircuitModelId& sram_model, + const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index 8aad4fea7..b9f679887 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -490,7 +490,8 @@ static void build_switch_block_module( /* Build a physical memory block */ if (group_config_block) { add_physical_memory_module(module_manager, decoder_lib, sb_module, - circuit_lib, sram_orgz_type, sram_model); + circuit_lib, sram_orgz_type, sram_model, + verbose); } /* Add global ports to the pb_module: @@ -1010,7 +1011,8 @@ static void build_connection_block_module( /* Build a physical memory block */ if (group_config_block) { add_physical_memory_module(module_manager, decoder_lib, cb_module, - circuit_lib, sram_orgz_type, sram_model); + circuit_lib, sram_orgz_type, sram_model, + verbose); } /* Add global ports to the pb_module: diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index a26cc48a0..9acba8a1c 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -499,7 +499,7 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( int rec_find_physical_memory_children( const ModuleManager& module_manager, const ModuleId& curr_module, - std::vector& physical_memory_children) { + std::vector& physical_memory_children, const bool& verbose) { if (module_manager .configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL) @@ -522,9 +522,15 @@ int rec_find_physical_memory_children( physical_memory_children.push_back( module_manager.logical2physical_configurable_children( curr_module)[ichild]); + VTR_LOGV( + verbose, "Collecting physical memory module '%s'...\n", + module_manager + .module_name(module_manager.logical2physical_configurable_children( + curr_module)[ichild]) + .c_str()); } else { rec_find_physical_memory_children(module_manager, logical_child, - physical_memory_children); + physical_memory_children, verbose); } } return CMD_EXEC_SUCCESS; diff --git a/openfpga/src/utils/memory_utils.h b/openfpga/src/utils/memory_utils.h index 14243ec79..3578a8701 100644 --- a/openfpga/src/utils/memory_utils.h +++ b/openfpga/src/utils/memory_utils.h @@ -61,7 +61,7 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( */ int rec_find_physical_memory_children( const ModuleManager& module_manager, const ModuleId& curr_module, - std::vector& physical_memory_children); + std::vector& physical_memory_children, const bool& verbose); /** * @brief Update all the mappings between logical-to-physical memory children From a0f81a5bf295586ab1ff9d12c9fd1d6c1c9308c7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Aug 2023 13:34:38 -0700 Subject: [PATCH 20/41] [core] now verilog generator can output feedthrough memory module to files --- openfpga/src/fabric/build_memory_modules.cpp | 6 +-- openfpga/src/fpga_verilog/verilog_memory.cpp | 45 +++++++++++++++++++- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 35e272d55..14eac8409 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1508,7 +1508,7 @@ int add_physical_memory_module(ModuleManager& module_manager, for (size_t ichild = 0; ichild < module_manager .configurable_children( - curr_module, ModuleManager::e_config_child_type::PHYSICAL) + curr_module, ModuleManager::e_config_child_type::LOGICAL) .size(); ++ichild) { for (e_circuit_model_port_type port_type : @@ -1526,9 +1526,9 @@ int add_physical_memory_module(ModuleManager& module_manager, module_manager.module_port(phy_mem_module, src_port_id); ModuleId des_module = module_manager.configurable_children( - curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; size_t des_instance = module_manager.configurable_child_instances( - curr_module, ModuleManager::e_config_child_type::PHYSICAL)[ichild]; + curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; ModulePortId des_port_id = module_manager.find_module_port(des_module, des_port_name); if (!module_manager.valid_module_port_id(des_module, des_port_id)) { diff --git a/openfpga/src/fpga_verilog/verilog_memory.cpp b/openfpga/src/fpga_verilog/verilog_memory.cpp index ef6903cd5..509cc4365 100644 --- a/openfpga/src/fpga_verilog/verilog_memory.cpp +++ b/openfpga/src/fpga_verilog/verilog_memory.cpp @@ -17,6 +17,7 @@ #include "mux_utils.h" #include "openfpga_digest.h" #include "openfpga_naming.h" +#include "openfpga_reserved_words.h" #include "verilog_constants.h" #include "verilog_memory.h" #include "verilog_module_writer.h" @@ -51,7 +52,7 @@ static void print_verilog_mux_memory_module( circuit_lib, mux_model, find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()), - std::string(VERILOG_MEM_POSTFIX)); + std::string(MEMORY_MODULE_POSTFIX)); ModuleId mem_module = module_manager.find_module(module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); /* Write the module content in Verilog format */ @@ -63,6 +64,28 @@ static void print_verilog_mux_memory_module( /* Add an empty line as a splitter */ fp << std::endl; + + /* Print feedthrough memory if exists */ + std::string feedthru_module_name = generate_mux_subckt_name( + circuit_lib, mux_model, + find_mux_num_datapath_inputs(circuit_lib, mux_model, + mux_graph.num_inputs()), + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + ModuleId feedthru_mem_module = + module_manager.find_module(feedthru_module_name); + if (module_manager.valid_module_id(feedthru_mem_module)) { + VTR_ASSERT(true == module_manager.valid_module_id(feedthru_mem_module)); + /* Write the module content in Verilog format */ + write_verilog_module_to_file( + fp, module_manager, feedthru_mem_module, + options.explicit_port_mapping() || + circuit_lib.dump_explicit_port_map(mux_model), + options.default_net_type()); + + /* Add an empty line as a splitter */ + fp << std::endl; + } + break; } case CIRCUIT_MODEL_DESIGN_RRAM: @@ -174,7 +197,7 @@ void print_verilog_submodule_memories(const ModuleManager& module_manager, /* Create the module name for the memory block */ std::string module_name = generate_memory_module_name( - circuit_lib, model, sram_models[0], std::string(VERILOG_MEM_POSTFIX)); + circuit_lib, model, sram_models[0], std::string(MEMORY_MODULE_POSTFIX)); ModuleId mem_module = module_manager.find_module(module_name); VTR_ASSERT(true == module_manager.valid_module_id(mem_module)); @@ -186,6 +209,24 @@ void print_verilog_submodule_memories(const ModuleManager& module_manager, /* Add an empty line as a splitter */ fp << std::endl; + + /* Create the module name for the memory block */ + std::string feedthru_module_name = generate_memory_module_name( + circuit_lib, model, sram_models[0], + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + + ModuleId feedthru_mem_module = + module_manager.find_module(feedthru_module_name); + if (module_manager.valid_module_id(feedthru_mem_module)) { + /* Write the module content in Verilog format */ + write_verilog_module_to_file(fp, module_manager, feedthru_mem_module, + options.explicit_port_mapping() || + circuit_lib.dump_explicit_port_map(model), + options.default_net_type()); + + /* Add an empty line as a splitter */ + fp << std::endl; + } } /* Close the file stream */ From 64c0839e306ea4b8e0d6023d8d17ccfa445002cd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Aug 2023 16:11:33 -0700 Subject: [PATCH 21/41] [core] now verilog writer supports memory group modules --- openfpga/src/fabric/build_memory_modules.cpp | 25 ++++++++++++++------ openfpga/src/fabric/module_manager.cpp | 11 +++++++++ openfpga/src/fabric/module_manager.h | 10 ++++++-- openfpga/src/fpga_verilog/verilog_memory.cpp | 12 ++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 14eac8409..95f15a69b 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1318,6 +1318,10 @@ int build_memory_group_module(ModuleManager& module_manager, return CMD_EXEC_FATAL_ERROR; } + /* Label module usage */ + module_manager.set_module_usage(mem_module, + ModuleManager::MODULE_CONFIG_GROUP); + /* Add output ports */ std::string out_port_name = generate_configurable_memory_data_out_name(); BasicPort out_port(out_port_name, num_mems); @@ -1499,7 +1503,9 @@ int add_physical_memory_module(ModuleManager& module_manager, /* Build nets between the data output of the physical memory module and the * outputs of the logical configurable children */ - size_t curr_mem_pin_index = 0; + std::map curr_mem_pin_index; + curr_mem_pin_index[CIRCUIT_MODEL_PORT_BL] = 0; + curr_mem_pin_index[CIRCUIT_MODEL_PORT_BLB] = 0; std::map mem2mem_port_map; mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME); @@ -1511,6 +1517,11 @@ int add_physical_memory_module(ModuleManager& module_manager, curr_module, ModuleManager::e_config_child_type::LOGICAL) .size(); ++ichild) { + ModuleId des_module = module_manager.configurable_children( + curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; + size_t des_instance = module_manager.configurable_child_instances( + curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; + for (e_circuit_model_port_type port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) { std::string src_port_name = mem2mem_port_map[port_type]; @@ -1525,10 +1536,6 @@ int add_physical_memory_module(ModuleManager& module_manager, BasicPort src_port = module_manager.module_port(phy_mem_module, src_port_id); - ModuleId des_module = module_manager.configurable_children( - curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; - size_t des_instance = module_manager.configurable_child_instances( - curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; ModulePortId des_port_id = module_manager.find_module_port(des_module, des_port_name); if (!module_manager.valid_module_port_id(des_module, des_port_id)) { @@ -1540,7 +1547,7 @@ int add_physical_memory_module(ModuleManager& module_manager, /* Create a net and add source and sink to it */ ModuleNetId net = create_module_source_pin_net( module_manager, curr_module, phy_mem_module, phy_mem_instance, - src_port_id, src_port.pins()[curr_mem_pin_index]); + src_port_id, src_port.pins()[curr_mem_pin_index[port_type]]); if (module_manager.valid_module_net_id(curr_module, net)) { return CMD_EXEC_FATAL_ERROR; } @@ -1548,10 +1555,14 @@ int add_physical_memory_module(ModuleManager& module_manager, module_manager.add_module_net_sink(curr_module, net, des_module, des_instance, des_port_id, des_port.pins()[ipin]); - curr_mem_pin_index++; + curr_mem_pin_index[port_type]++; } } } + VTR_ASSERT(curr_mem_pin_index[CIRCUIT_MODEL_PORT_BL] == + module_num_config_bits); + VTR_ASSERT(curr_mem_pin_index[CIRCUIT_MODEL_PORT_BLB] == + module_num_config_bits); /* TODO: Recursively update the logical configurable child with the physical * memory module parent and its instance id */ diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 2e8ca26cc..10b55c7f9 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -26,6 +26,17 @@ ModuleManager::module_range ModuleManager::modules() const { return vtr::make_range(ids_.begin(), ids_.end()); } +std::vector ModuleManager::modules_by_usage( + const ModuleManager::e_module_usage_type& usage) const { + std::vector module_list; + for (ModuleId curr_module : ids_) { + if (usages_[curr_module] == usage) { + module_list.push_back(curr_module); + } + } + return module_list; +} + /* Find all the ports belonging to a module */ ModuleManager::module_port_range ModuleManager::module_ports( const ModuleId& module) const { diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 02f8dfb5b..c79ce6b4e 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -53,8 +53,9 @@ class ModuleManager { * port should be applied to modules */ enum e_module_usage_type { - MODULE_TOP, /* Top-level module */ - MODULE_CONFIG, /* Configuration modules, i.e., decoders, sram etc. */ + MODULE_TOP, /* Top-level module */ + MODULE_CONFIG, /* Configuration modules, i.e., decoders, sram etc. */ + MODULE_CONFIG_GROUP, /* Configuration modules, i.e., decoders, sram etc. */ MODULE_INTERC, /* Programmable interconnection, e.g., routing multiplexer etc. */ MODULE_GRID, /* Grids (programmable blocks) */ @@ -165,6 +166,11 @@ class ModuleManager { public: /* Public aggregators */ /* Find all the modules */ module_range modules() const; + /** @brief find all the modules with a given usage. Note that this function is + * not optimized when the number of modules are large. In most cases, the + * number of modules are fairly small (less than 10k). */ + std::vector modules_by_usage( + const ModuleManager::e_module_usage_type& usage) const; /* Find all the ports belonging to a module */ module_port_range module_ports(const ModuleId& module) const; /* Find all the nets belonging to a module */ diff --git a/openfpga/src/fpga_verilog/verilog_memory.cpp b/openfpga/src/fpga_verilog/verilog_memory.cpp index 509cc4365..a257c60a9 100644 --- a/openfpga/src/fpga_verilog/verilog_memory.cpp +++ b/openfpga/src/fpga_verilog/verilog_memory.cpp @@ -229,6 +229,18 @@ void print_verilog_submodule_memories(const ModuleManager& module_manager, } } + /* Include memory group modules */ + for (ModuleId mem_group_module : module_manager.modules_by_usage( + ModuleManager::e_module_usage_type::MODULE_CONFIG_GROUP)) { + /* Write the module content in Verilog format */ + write_verilog_module_to_file(fp, module_manager, mem_group_module, + options.explicit_port_mapping(), + options.default_net_type()); + + /* Add an empty line as a splitter */ + fp << std::endl; + } + /* Close the file stream */ fp.close(); From bb9cf6dbcbde5681c42cdd2579192a15079a9885 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Aug 2023 16:45:15 -0700 Subject: [PATCH 22/41] [core] fixed a critical bug which causes undriven nets on config bus in group config block --- openfpga/src/fabric/build_memory_modules.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 95f15a69b..9bfc45e6f 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1544,11 +1544,15 @@ int add_physical_memory_module(ModuleManager& module_manager, BasicPort des_port = module_manager.module_port(des_module, des_port_id); /* Build nets */ for (size_t ipin = 0; ipin < des_port.pins().size(); ++ipin) { + VTR_LOGV(verbose, + "Building net '%s[%lu].%s[%lu]' -> '%s[%lu].%s[%lu]\n", + module_manager.module_name(phy_mem_module).c_str(), phy_mem_instance, src_port.get_name().c_str(), curr_mem_pin_index[port_type], + module_manager.module_name(des_module).c_str(), des_instance, des_port.get_name().c_str(), des_port.pins()[ipin]); /* Create a net and add source and sink to it */ ModuleNetId net = create_module_source_pin_net( module_manager, curr_module, phy_mem_module, phy_mem_instance, src_port_id, src_port.pins()[curr_mem_pin_index[port_type]]); - if (module_manager.valid_module_net_id(curr_module, net)) { + if (!module_manager.valid_module_net_id(curr_module, net)) { return CMD_EXEC_FATAL_ERROR; } /* Add net sink */ From 7d8d686f74b8e53c2cc24420b9ec936af7a9bc66 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Aug 2023 16:52:43 -0700 Subject: [PATCH 23/41] [core] add status codes to build grid modules --- openfpga/src/fabric/build_device_module.cpp | 13 ++++--- openfpga/src/fabric/build_grid_modules.cpp | 37 ++++++++++++++------ openfpga/src/fabric/build_grid_modules.h | 2 +- openfpga/src/fabric/build_memory_modules.cpp | 10 +++--- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index fbe3d85eb..e4ac8d75b 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -82,11 +82,14 @@ int build_device_module_graph( group_config_block, verbose); /* Build grid and programmable block modules */ - build_grid_modules(module_manager, decoder_lib, vpr_device_ctx, - openfpga_ctx.vpr_device_annotation(), - openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), - openfpga_ctx.arch().config_protocol.type(), sram_model, - duplicate_grid_pin, group_config_block, verbose); + status = build_grid_modules( + module_manager, decoder_lib, vpr_device_ctx, + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.arch().circuit_lib, + openfpga_ctx.mux_lib(), openfpga_ctx.arch().config_protocol.type(), + sram_model, duplicate_grid_pin, group_config_block, verbose); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } if (true == compress_routing) { build_unique_routing_modules(module_manager, decoder_lib, vpr_device_ctx, diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index fa8c6a94c..0d3259a90 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -6,6 +6,7 @@ #include /* Headers from vtrutil library */ +#include "command_exit_codes.h" #include "vtr_assert.h" #include "vtr_geometry.h" #include "vtr_log.h" @@ -1137,7 +1138,7 @@ static void rec_build_logical_tile_modules( * The param 'border_side' is required, which is specify which side of fabric * the I/O block locates at. *****************************************************************************/ -static void build_physical_tile_module( +static int build_physical_tile_module( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const VprDeviceAnnotation& vpr_device_annotation, const CircuitLibrary& circuit_lib, @@ -1145,6 +1146,7 @@ static void build_physical_tile_module( const CircuitModelId& sram_model, t_physical_tile_type_ptr phy_block_type, const e_side& border_side, const bool& duplicate_grid_pin, const bool& group_config_block, const bool& verbose) { + int status = CMD_EXEC_SUCCESS; /* Create a Module for the top-level physical block, and add to module manager */ std::string grid_module_name = generate_grid_block_module_name( @@ -1209,9 +1211,12 @@ static void build_physical_tile_module( /* TODO: Add a physical memory block */ if (group_config_block) { - add_physical_memory_module(module_manager, decoder_lib, grid_module, - circuit_lib, sram_orgz_type, sram_model, - verbose); + status = add_physical_memory_module(module_manager, decoder_lib, + grid_module, circuit_lib, + sram_orgz_type, sram_model, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } } /* Add grid ports(pins) to the module */ @@ -1324,6 +1329,8 @@ static void build_physical_tile_module( } VTR_LOGV(verbose, "Done\n"); + + return status; } /***************************************************************************** @@ -1339,7 +1346,7 @@ static void build_physical_tile_module( * - Only one module for each CLB (FILL_TYPE) * - Only one module for each heterogeneous block ****************************************************************************/ -void build_grid_modules( +int build_grid_modules( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, @@ -1349,6 +1356,8 @@ void build_grid_modules( /* Start time count */ vtr::ScopedStartFinishTimer timer("Build grid modules"); + int status = CMD_EXEC_SUCCESS; + /* Enumerate the types of logical tiles, and build a module for each * Build modules for all the pb_types/pb_graph_nodes * use a Depth-First Search Algorithm to print the sub-modules @@ -1396,20 +1405,28 @@ void build_grid_modules( std::set io_type_sides = find_physical_io_tile_located_sides(device_ctx.grid, &physical_tile); for (const e_side& io_type_side : io_type_sides) { - build_physical_tile_module( + status = build_physical_tile_module( module_manager, decoder_lib, device_annotation, circuit_lib, sram_orgz_type, sram_model, &physical_tile, io_type_side, duplicate_grid_pin, group_config_block, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } } } else { /* For CLB and heterogenenous blocks */ - build_physical_tile_module(module_manager, decoder_lib, device_annotation, - circuit_lib, sram_orgz_type, sram_model, - &physical_tile, NUM_SIDES, duplicate_grid_pin, - group_config_block, verbose); + status = build_physical_tile_module( + module_manager, decoder_lib, device_annotation, circuit_lib, + sram_orgz_type, sram_model, &physical_tile, NUM_SIDES, + duplicate_grid_pin, group_config_block, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } } } VTR_LOG("Done\n"); + + return status; } } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_grid_modules.h b/openfpga/src/fabric/build_grid_modules.h index 94ac49dc2..d83cc3ac3 100644 --- a/openfpga/src/fabric/build_grid_modules.h +++ b/openfpga/src/fabric/build_grid_modules.h @@ -17,7 +17,7 @@ /* begin namespace openfpga */ namespace openfpga { -void build_grid_modules( +int build_grid_modules( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 9bfc45e6f..d85e3fa40 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1544,10 +1544,12 @@ int add_physical_memory_module(ModuleManager& module_manager, BasicPort des_port = module_manager.module_port(des_module, des_port_id); /* Build nets */ for (size_t ipin = 0; ipin < des_port.pins().size(); ++ipin) { - VTR_LOGV(verbose, - "Building net '%s[%lu].%s[%lu]' -> '%s[%lu].%s[%lu]\n", - module_manager.module_name(phy_mem_module).c_str(), phy_mem_instance, src_port.get_name().c_str(), curr_mem_pin_index[port_type], - module_manager.module_name(des_module).c_str(), des_instance, des_port.get_name().c_str(), des_port.pins()[ipin]); + VTR_LOGV( + verbose, "Building net '%s[%lu].%s[%lu]' -> '%s[%lu].%s[%lu]\n", + module_manager.module_name(phy_mem_module).c_str(), phy_mem_instance, + src_port.get_name().c_str(), curr_mem_pin_index[port_type], + module_manager.module_name(des_module).c_str(), des_instance, + des_port.get_name().c_str(), des_port.pins()[ipin]); /* Create a net and add source and sink to it */ ModuleNetId net = create_module_source_pin_net( module_manager, curr_module, phy_mem_module, phy_mem_instance, From 9a23dc7bff791a7bc7f70117e3de848393ff3c4a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Aug 2023 21:20:21 -0700 Subject: [PATCH 24/41] [core] fixed some bugs which causes architecture bitstream generation failed when supporting group_config_block --- openfpga/src/fabric/build_memory_modules.cpp | 3 + .../fpga_bitstream/build_grid_bitstream.cpp | 74 +++++++++++++------ .../build_routing_bitstream.cpp | 51 +++++++++---- 3 files changed, 94 insertions(+), 34 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index d85e3fa40..e9416297c 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1495,6 +1495,9 @@ int add_physical_memory_module(ModuleManager& module_manager, size_t phy_mem_instance = module_manager.num_instance(curr_module, phy_mem_module); module_manager.add_child_module(curr_module, phy_mem_module, false); + /* TODO: Give a more meaningful instance name? */ + module_manager.set_child_instance_name(curr_module, phy_mem_module, + phy_mem_instance, phy_mem_module_name); /* Register in the physical configurable children list */ module_manager.add_configurable_child( diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index cff41f1ec..4067a663e 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -53,7 +53,8 @@ static void build_primitive_bitstream( const ConfigBlockId& parent_configurable_block, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& device_annotation, const PhysicalPb& physical_pb, - const PhysicalPbId& primitive_pb_id, t_pb_type* primitive_pb_type) { + const PhysicalPbId& primitive_pb_id, t_pb_type* primitive_pb_type, + const bool& verbose) { /* Ensure a valid physical pritimive pb */ if (nullptr == primitive_pb_type) { VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid primitive_pb_type!\n"); @@ -136,6 +137,11 @@ static void build_primitive_bitstream( ConfigBlockId mem_block = bitstream_manager.add_block(mem_block_name); bitstream_manager.add_child_block(parent_configurable_block, mem_block); + VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", + mode_select_bitstream.size(), + bitstream_manager.block_name(mem_block).c_str(), + bitstream_manager.block_name(parent_configurable_block).c_str()); + /* Add the bitstream to the bitstream manager */ bitstream_manager.add_block_bits(mem_block, mode_select_bitstream); } @@ -159,7 +165,7 @@ static void build_physical_block_pin_interc_bitstream( const VprDeviceAnnotation& device_annotation, const VprBitstreamAnnotation& bitstream_annotation, const PhysicalPb& physical_pb, t_pb_graph_pin* des_pb_graph_pin, - t_mode* physical_mode) { + t_mode* physical_mode, const bool& verbose) { /* Identify the number of fan-in (Consider interconnection edges of only * selected mode) */ t_interconnect* cur_interc = @@ -275,6 +281,11 @@ static void build_physical_block_pin_interc_bitstream( module_manager.module_port(mux_mem_module, mux_mem_out_port_id) .get_width()); + VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", + mux_bitstream.size(), + bitstream_manager.block_name(mux_mem_block).c_str(), + bitstream_manager.block_name(parent_configurable_block).c_str()); + /* Add the bistream to the bitstream manager */ bitstream_manager.add_block_bits(mux_mem_block, mux_bitstream); /* Record path ids, input and output nets */ @@ -330,7 +341,8 @@ static void build_physical_block_interc_port_bitstream( const VprDeviceAnnotation& device_annotation, const VprBitstreamAnnotation& bitstream_annotation, t_pb_graph_node* physical_pb_graph_node, const PhysicalPb& physical_pb, - const e_circuit_pb_port_type& pb_port_type, t_mode* physical_mode) { + const e_circuit_pb_port_type& pb_port_type, t_mode* physical_mode, + const bool& verbose) { switch (pb_port_type) { case CIRCUIT_PB_PORT_INPUT: for (int iport = 0; iport < physical_pb_graph_node->num_input_ports; @@ -341,7 +353,8 @@ static void build_physical_block_interc_port_bitstream( bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb, - &(physical_pb_graph_node->input_pins[iport][ipin]), physical_mode); + &(physical_pb_graph_node->input_pins[iport][ipin]), physical_mode, + verbose); } } break; @@ -354,7 +367,8 @@ static void build_physical_block_interc_port_bitstream( bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb, - &(physical_pb_graph_node->output_pins[iport][ipin]), physical_mode); + &(physical_pb_graph_node->output_pins[iport][ipin]), physical_mode, + verbose); } } break; @@ -367,7 +381,8 @@ static void build_physical_block_interc_port_bitstream( bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb, - &(physical_pb_graph_node->clock_pins[iport][ipin]), physical_mode); + &(physical_pb_graph_node->clock_pins[iport][ipin]), physical_mode, + verbose); } } break; @@ -389,7 +404,7 @@ static void build_physical_block_interc_bitstream( const VprDeviceAnnotation& device_annotation, const VprBitstreamAnnotation& bitstream_annotation, t_pb_graph_node* physical_pb_graph_node, const PhysicalPb& physical_pb, - t_mode* physical_mode) { + t_mode* physical_mode, const bool& verbose) { /* Check if the pb_graph node is valid or not */ if (nullptr == physical_pb_graph_node) { VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid physical_pb_graph_node.\n"); @@ -409,7 +424,8 @@ static void build_physical_block_interc_bitstream( build_physical_block_interc_port_bitstream( bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, - physical_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_OUTPUT, physical_mode); + physical_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_OUTPUT, physical_mode, + verbose); /* We check input_pins of child_pb_graph_node and its the input_edges * Iterate over the interconnections between inputs of physical_pb_graph_node @@ -431,12 +447,14 @@ static void build_physical_block_interc_bitstream( build_physical_block_interc_port_bitstream( bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, - child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_INPUT, physical_mode); + child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_INPUT, physical_mode, + verbose); /* For clock pins, we should do the same work */ build_physical_block_interc_port_bitstream( bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, - child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_CLOCK, physical_mode); + child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_CLOCK, physical_mode, + verbose); } } } @@ -453,7 +471,7 @@ static void build_lut_bitstream(BitstreamManager& bitstream_manager, const MuxLibrary& mux_lib, const PhysicalPb& physical_pb, const PhysicalPbId& lut_pb_id, - t_pb_type* lut_pb_type) { + t_pb_type* lut_pb_type, const bool& verbose) { /* Ensure a valid physical pritimive pb */ if (nullptr == lut_pb_type) { VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid lut_pb_type!\n"); @@ -599,6 +617,10 @@ static void build_lut_bitstream(BitstreamManager& bitstream_manager, ConfigBlockId mem_block = bitstream_manager.add_block(mem_block_name); bitstream_manager.add_child_block(parent_configurable_block, mem_block); + VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", lut_bitstream.size(), + bitstream_manager.block_name(mem_block).c_str(), + bitstream_manager.block_name(parent_configurable_block).c_str()); + /* Add the bitstream to the bitstream manager */ bitstream_manager.add_block_bits(mem_block, lut_bitstream); } @@ -622,7 +644,8 @@ static void rec_build_physical_block_bitstream( const VprDeviceAnnotation& device_annotation, const VprBitstreamAnnotation& bitstream_annotation, const e_side& border_side, const PhysicalPb& physical_pb, const PhysicalPbId& pb_id, - t_pb_graph_node* physical_pb_graph_node, const size_t& pb_graph_node_index) { + t_pb_graph_node* physical_pb_graph_node, const size_t& pb_graph_node_index, + const bool& verbose) { /* Get the physical pb_type that is linked to the pb_graph node */ t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type; @@ -681,7 +704,7 @@ static void rec_build_physical_block_bitstream( border_side, physical_pb, child_pb, &(physical_pb_graph_node ->child_pb_graph_nodes[physical_mode->index][ipb][jpb]), - jpb); + jpb, verbose); } } } @@ -698,7 +721,8 @@ static void rec_build_physical_block_bitstream( */ build_lut_bitstream(bitstream_manager, pb_configurable_block, device_annotation, module_manager, circuit_lib, - mux_lib, physical_pb, pb_id, physical_pb_type); + mux_lib, physical_pb, pb_id, physical_pb_type, + verbose); break; case CIRCUIT_MODEL_FF: case CIRCUIT_MODEL_HARDLOGIC: @@ -706,7 +730,7 @@ static void rec_build_physical_block_bitstream( /* For other types of blocks, we can apply a generic therapy */ build_primitive_bitstream( bitstream_manager, pb_configurable_block, module_manager, circuit_lib, - device_annotation, physical_pb, pb_id, physical_pb_type); + device_annotation, physical_pb, pb_id, physical_pb_type, verbose); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, @@ -722,7 +746,7 @@ static void rec_build_physical_block_bitstream( build_physical_block_interc_bitstream( bitstream_manager, pb_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, - physical_pb_graph_node, physical_pb, physical_mode); + physical_pb_graph_node, physical_pb, physical_mode, verbose); } /******************************************************************** @@ -740,7 +764,8 @@ static void build_physical_block_bitstream( const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, const VprBitstreamAnnotation& bitstream_annotation, const DeviceGrid& grids, - const vtr::Point& grid_coord, const e_side& border_side) { + const vtr::Point& grid_coord, const e_side& border_side, + const bool& verbose) { /* Create a block for the grid in bitstream manager */ t_physical_tile_type_ptr grid_type = grids.get_physical_type(grid_coord.x(), grid_coord.y()); @@ -795,6 +820,11 @@ static void build_physical_block_bitstream( grid_module, ModuleManager::e_config_child_type::PHYSICAL)[0]); ConfigBlockId grid_grouped_config_block = bitstream_manager.add_block(phy_mem_instance_name); + VTR_LOGV( + verbose, + "Added grouped configurable memory block '%s' as a child to '%s'\n", + bitstream_manager.block_name(grid_grouped_config_block).c_str(), + bitstream_manager.block_name(grid_configurable_block).c_str()); bitstream_manager.add_child_block(grid_configurable_block, grid_grouped_config_block); grid_configurable_block = grid_grouped_config_block; @@ -827,7 +857,7 @@ static void build_physical_block_bitstream( bitstream_manager, grid_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, border_side, PhysicalPb(), - PhysicalPbId::INVALID(), lb_type->pb_graph_head, z); + PhysicalPbId::INVALID(), lb_type->pb_graph_head, z, verbose); } else { const PhysicalPb& phy_pb = cluster_annotation.physical_pb( place_annotation.grid_blocks(grid_coord)[z]); @@ -842,7 +872,7 @@ static void build_physical_block_bitstream( bitstream_manager, grid_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, border_side, phy_pb, top_pb_id, pb_graph_head, - z); + z, verbose); } } } @@ -901,7 +931,8 @@ void build_grid_bitstream( build_physical_block_bitstream( bitstream_manager, parent_block, module_manager, fabric_tile, curr_tile, circuit_lib, mux_lib, atom_ctx, device_annotation, cluster_annotation, - place_annotation, bitstream_annotation, grids, grid_coord, NUM_SIDES); + place_annotation, bitstream_annotation, grids, grid_coord, NUM_SIDES, + verbose); } } VTR_LOGV(verbose, "Done\n"); @@ -947,7 +978,8 @@ void build_grid_bitstream( build_physical_block_bitstream( bitstream_manager, parent_block, module_manager, fabric_tile, curr_tile, circuit_lib, mux_lib, atom_ctx, device_annotation, cluster_annotation, - place_annotation, bitstream_annotation, grids, io_coordinate, io_side); + place_annotation, bitstream_annotation, grids, io_coordinate, io_side, + verbose); } } VTR_LOGV(verbose, "Done\n"); diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index 5716f8743..620cafed3 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -37,7 +37,7 @@ static void build_switch_block_mux_bitstream( const MuxLibrary& mux_lib, const RRGraphView& rr_graph, const RRNodeId& cur_rr_node, const std::vector& drive_rr_nodes, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, - const VprRoutingAnnotation& routing_annotation) { + const VprRoutingAnnotation& routing_annotation, const bool& verbose) { /* Check current rr_node is CHANX or CHANY*/ VTR_ASSERT((CHANX == rr_graph.node_type(cur_rr_node)) || (CHANY == rr_graph.node_type(cur_rr_node))); @@ -102,6 +102,9 @@ static void build_switch_block_mux_bitstream( module_manager.module_port(mux_mem_module, mux_mem_out_port_id) .get_width()); + VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), + bitstream_manager.block_name(mux_mem_block).c_str()); + /* Add the bistream to the bitstream manager */ bitstream_manager.add_block_bits(mux_mem_block, mux_bitstream); /* Record path ids, input and output nets */ @@ -150,7 +153,7 @@ static void build_switch_block_interc_bitstream( const MuxLibrary& mux_lib, const RRGraphView& rr_graph, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprRoutingAnnotation& routing_annotation, const RRGSB& rr_gsb, - const e_side& chan_side, const size_t& chan_node_id) { + const e_side& chan_side, const size_t& chan_node_id, const bool& verbose) { std::vector driver_rr_nodes; /* Get the node */ @@ -179,11 +182,14 @@ static void build_switch_block_interc_bitstream( std::string("")); ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name); bitstream_manager.add_child_block(sb_configurable_block, mux_mem_block); + VTR_LOGV(verbose, "Added '%s' under '%s'\n", + bitstream_manager.block_name(mux_mem_block).c_str(), + bitstream_manager.block_name(sb_configurable_block).c_str()); /* This is a routing multiplexer! Generate bitstream */ build_switch_block_mux_bitstream( bitstream_manager, mux_mem_block, module_manager, circuit_lib, mux_lib, rr_graph, cur_rr_node, driver_rr_nodes, atom_ctx, device_annotation, - routing_annotation); + routing_annotation, verbose); } /*Nothing should be done else*/ } @@ -204,7 +210,7 @@ static void build_switch_block_bitstream( const MuxLibrary& mux_lib, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprRoutingAnnotation& routing_annotation, const RRGraphView& rr_graph, - const RRGSB& rr_gsb) { + const RRGSB& rr_gsb, const bool& verbose) { /* Iterate over all the multiplexers */ for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { SideManager side_manager(side); @@ -222,7 +228,7 @@ static void build_switch_block_bitstream( build_switch_block_interc_bitstream( bitstream_manager, sb_config_block, module_manager, circuit_lib, mux_lib, rr_graph, atom_ctx, device_annotation, routing_annotation, - rr_gsb, side_manager.get_side(), itrack); + rr_gsb, side_manager.get_side(), itrack, verbose); } } } @@ -240,7 +246,8 @@ static void build_connection_block_mux_bitstream( const MuxLibrary& mux_lib, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprRoutingAnnotation& routing_annotation, const RRGraphView& rr_graph, - const RRGSB& rr_gsb, const e_side& cb_ipin_side, const size_t& ipin_index) { + const RRGSB& rr_gsb, const e_side& cb_ipin_side, const size_t& ipin_index, + const bool& verbose) { RRNodeId src_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, ipin_index); /* Find drive_rr_nodes*/ std::vector driver_rr_edges = @@ -308,6 +315,9 @@ static void build_connection_block_mux_bitstream( module_manager.module_port(mux_mem_module, mux_mem_out_port_id) .get_width()); + VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), + bitstream_manager.block_name(mux_mem_block).c_str()); + /* Add the bistream to the bitstream manager */ bitstream_manager.add_block_bits(mux_mem_block, mux_bitstream); /* Record path ids, input and output nets */ @@ -381,11 +391,14 @@ static void build_connection_block_interc_bitstream( std::string("")); ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name); bitstream_manager.add_child_block(cb_configurable_block, mux_mem_block); + VTR_LOGV(verbose, "Added '%s' under '%s'\n", + bitstream_manager.block_name(mux_mem_block).c_str(), + bitstream_manager.block_name(cb_configurable_block).c_str()); /* This is a routing multiplexer! Generate bitstream */ build_connection_block_mux_bitstream( bitstream_manager, mux_mem_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, routing_annotation, rr_graph, rr_gsb, - cb_ipin_side, ipin_index); + cb_ipin_side, ipin_index, verbose); } /*Nothing should be done else*/ } @@ -488,7 +501,10 @@ static void build_connection_block_bitstreams( /* Bypass empty blocks which have none configurable children */ if (0 == count_module_manager_module_configurable_children( module_manager, cb_module, - ModuleManager::e_config_child_type::LOGICAL)) { + ModuleManager::e_config_child_type::LOGICAL) && + 0 == count_module_manager_module_configurable_children( + module_manager, cb_module, + ModuleManager::e_config_child_type::PHYSICAL)) { continue; } @@ -549,6 +565,9 @@ static void build_connection_block_bitstreams( bitstream_manager.add_block(phy_mem_instance_name); bitstream_manager.add_child_block(cb_configurable_block, cb_grouped_config_block); + VTR_LOGV(verbose, "Added '%s' as a child to '%s'\n", + bitstream_manager.block_name(cb_grouped_config_block).c_str(), + bitstream_manager.block_name(cb_configurable_block).c_str()); cb_configurable_block = cb_grouped_config_block; } @@ -617,7 +636,10 @@ void build_routing_bitstream( /* Bypass empty blocks which have none configurable children */ if (0 == count_module_manager_module_configurable_children( module_manager, sb_module, - ModuleManager::e_config_child_type::LOGICAL)) { + ModuleManager::e_config_child_type::LOGICAL) && + 0 == count_module_manager_module_configurable_children( + module_manager, sb_module, + ModuleManager::e_config_child_type::PHYSICAL)) { continue; } @@ -673,13 +695,16 @@ void build_routing_bitstream( bitstream_manager.add_block(phy_mem_instance_name); bitstream_manager.add_child_block(sb_configurable_block, sb_grouped_config_block); + VTR_LOGV(verbose, "Added '%s' as a child to '%s'\n", + bitstream_manager.block_name(sb_grouped_config_block).c_str(), + bitstream_manager.block_name(sb_configurable_block).c_str()); sb_configurable_block = sb_grouped_config_block; } - build_switch_block_bitstream(bitstream_manager, sb_configurable_block, - module_manager, circuit_lib, mux_lib, - atom_ctx, device_annotation, - routing_annotation, rr_graph, rr_gsb); + build_switch_block_bitstream( + bitstream_manager, sb_configurable_block, module_manager, circuit_lib, + mux_lib, atom_ctx, device_annotation, routing_annotation, rr_graph, + rr_gsb, verbose); VTR_LOGV(verbose, "\tDone\n"); } From f4d7ad2bd1dc1d544cd306222de15625136367ca Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Aug 2023 13:38:51 -0700 Subject: [PATCH 25/41] [core] trying to fix the bug on instance naming so that bitstream generation can work --- openfpga/src/fabric/build_grid_modules.cpp | 7 ++ openfpga/src/fabric/build_memory_modules.cpp | 55 +++++++------ openfpga/src/fabric/build_memory_modules.h | 1 + openfpga/src/fabric/build_routing_modules.cpp | 12 +++ openfpga/src/fabric/module_manager.cpp | 61 +++----------- openfpga/src/fabric/module_manager.h | 32 +++----- .../fpga_bitstream/build_grid_bitstream.cpp | 82 +++++++++++++++---- openfpga/src/utils/memory_utils.cpp | 62 ++++---------- openfpga/src/utils/memory_utils.h | 19 ++--- 9 files changed, 158 insertions(+), 173 deletions(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 0d3259a90..118e55f9e 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -380,6 +380,8 @@ static void build_primitive_block_module( VTR_ASSERT(module_manager.valid_module_id(physical_memory_module)); module_manager.set_logical2physical_configurable_child( primitive_module, config_child_id, physical_memory_module); + module_manager.set_logical2physical_configurable_child_instance_name( + primitive_module, config_child_id, physical_memory_module_name); } } @@ -693,6 +695,11 @@ static void add_module_pb_graph_pin_interc( mux_mem_module_name.c_str(), phy_mem_module_name.c_str()); module_manager.set_logical2physical_configurable_child( pb_module, config_child_id, phy_mem_module); + std::string phy_mux_mem_instance_name = generate_pb_memory_instance_name( + GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), + false); + module_manager.set_logical2physical_configurable_child_instance_name( + pb_module, config_child_id, phy_mux_mem_instance_name); VTR_LOGV(verbose, "Now use a feedthrough memory for '%s'\n", phy_mem_module_name.c_str()); } diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index e9416297c..da0b1285b 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "build_decoder_modules.h" #include "circuit_library_utils.h" @@ -1310,6 +1311,7 @@ int build_memory_group_module(ModuleManager& module_manager, const std::string& module_name, const CircuitModelId& sram_model, const std::vector& child_modules, + const std::vector& child_instance_names, const size_t& num_mems, const bool& verbose) { VTR_LOGV(verbose, "Building memory group module '%s'...\n", module_name.c_str()); @@ -1334,6 +1336,28 @@ int build_memory_group_module(ModuleManager& module_manager, module_manager.add_port(mem_module, outb_port, ModuleManager::MODULE_OUTPUT_PORT); + /* Identify the duplicated instance name: This mainly comes from the grid modules, which contains multi-instanced blocks. Therefore, we just count the duplicated instance names and name each of them with a unique index, e.g., mem_lut -> mem_lut_0, mem_lut_1 etc. The only exception is for the uinque instance name, we keep the original instance name */ + std::vector unique_child_instance_names; + unique_child_instance_names.reserve(child_instance_names.size()); + std::map unique_child_instance_name_count; + for (std::string curr_inst_name : child_instance_names) { + unique_child_instance_name_count[curr_inst_name]++; + } + std::map unique_child_instance_name_scoreboard; + for (std::string curr_inst_name : child_instance_names) { + if (1 == unique_child_instance_name_count[curr_inst_name]) { + unique_child_instance_names.push_back(curr_inst_name); + unique_child_instance_name_scoreboard[curr_inst_name] = 1; + continue; + } + auto result = unique_child_instance_name_scoreboard.find(curr_inst_name); + if (result == unique_child_instance_name_scoreboard.end()) { + unique_child_instance_names.push_back(generate_instance_name(curr_inst_name, result->second)); + unique_child_instance_name_scoreboard[curr_inst_name]++; + } + } + VTR_ASSERT(unique_child_instance_names.size() == child_instance_names.size()); + /* Add nets between child module outputs and memory modules */ size_t mem_out_pin_start_index = 0; size_t mem_outb_pin_start_index = 0; @@ -1342,6 +1366,7 @@ int build_memory_group_module(ModuleManager& module_manager, size_t child_instance = module_manager.num_instance(mem_module, child_module); module_manager.add_child_module(mem_module, child_module, false); + module_manager.set_child_instance_name(mem_module, child_module, child_instance, unique_child_instance_names[ichild]); module_manager.add_configurable_child( mem_module, child_module, child_instance, ModuleManager::e_config_child_type::UNIFIED); @@ -1453,9 +1478,10 @@ int add_physical_memory_module(ModuleManager& module_manager, int status = CMD_EXEC_SUCCESS; std::vector required_phy_mem_modules; + std::vector required_phy_mem_instance_names; status = rec_find_physical_memory_children( static_cast(module_manager), curr_module, - required_phy_mem_modules, verbose); + required_phy_mem_modules, required_phy_mem_instance_names, verbose); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -1478,6 +1504,7 @@ int add_physical_memory_module(ModuleManager& module_manager, status = build_memory_group_module(module_manager, decoder_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules, + required_phy_mem_instance_names, module_num_config_bits, verbose); } if (status != CMD_EXEC_SUCCESS) { @@ -1573,32 +1600,6 @@ int add_physical_memory_module(ModuleManager& module_manager, VTR_ASSERT(curr_mem_pin_index[CIRCUIT_MODEL_PORT_BLB] == module_num_config_bits); - /* TODO: Recursively update the logical configurable child with the physical - * memory module parent and its instance id */ - std::map logical_mem_child_inst_count; - status = rec_update_logical_memory_children_with_physical_mapping( - module_manager, curr_module, phy_mem_module, logical_mem_child_inst_count); - if (status != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; - } - /* Sanity check */ - std::map required_mem_child_inst_count; - for (ModuleId curr_child_module : - module_manager.child_modules(phy_mem_module)) { - if (logical_mem_child_inst_count[curr_child_module] != - module_manager.num_instance(phy_mem_module, curr_child_module)) { - VTR_LOG_ERROR( - "Expect the %lu instances of module '%s' under its parent '%s' while " - "only updated %lu during logical-to-physical configurable child " - "mapping sync-up!\n", - module_manager.num_instance(phy_mem_module, curr_child_module), - module_manager.module_name(curr_child_module).c_str(), - module_manager.module_name(phy_mem_module).c_str(), - logical_mem_child_inst_count[curr_child_module]); - return CMD_EXEC_FATAL_ERROR; - } - } - return status; } diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index bd04ff62f..5a754fa27 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -37,6 +37,7 @@ int build_memory_group_module(ModuleManager& module_manager, const std::string& module_name, const CircuitModelId& sram_model, const std::vector& child_modules, + const std::vector& child_instance_names, const size_t& num_mems, const bool& verbose); int add_physical_memory_module(ModuleManager& module_manager, diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index b9f679887..174a3e7df 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -258,6 +258,11 @@ static void build_switch_block_mux_module( VTR_ASSERT(true == module_manager.valid_module_id(physical_mem_module)); module_manager.set_logical2physical_configurable_child( sb_module, config_child_id, physical_mem_module); + std::string physical_mem_instance_name = generate_sb_memory_instance_name( + SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string(""), + false); + module_manager.set_logical2physical_configurable_child_instance_name( + sb_module, config_child_id, physical_mem_instance_name); } } @@ -781,6 +786,13 @@ static void build_connection_block_mux_module( VTR_ASSERT(true == module_manager.valid_module_id(physical_mem_module)); module_manager.set_logical2physical_configurable_child( cb_module, config_child_id, physical_mem_module); + std::string physical_mem_instance_name = generate_cb_memory_instance_name( + CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, + get_rr_graph_single_node_side( + rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, ipin_index)), + ipin_index, std::string(""), false); + module_manager.set_logical2physical_configurable_child_instance_name( + cb_module, config_child_id, physical_mem_instance_name); } } diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 10b55c7f9..bc907b100 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -132,23 +132,13 @@ std::vector ModuleManager::logical2physical_configurable_children( } /* Find all the instances of configurable child modules under a parent module */ -std::vector -ModuleManager::logical2physical_configurable_child_instances( +std::vector +ModuleManager::logical2physical_configurable_child_instance_names( const ModuleId& parent_module) const { /* Validate the module_id */ VTR_ASSERT(valid_module_id(parent_module)); - return logical2physical_configurable_child_instances_[parent_module]; -} - -/* Find all the instances of configurable child modules under a parent module */ -std::vector -ModuleManager::logical2physical_configurable_child_parents( - const ModuleId& parent_module) const { - /* Validate the module_id */ - VTR_ASSERT(valid_module_id(parent_module)); - - return logical2physical_configurable_child_parents_[parent_module]; + return logical2physical_configurable_child_instance_names_[parent_module]; } /* Find all the configurable child modules under a parent module */ @@ -741,8 +731,7 @@ ModuleId ModuleManager::add_module(const std::string& name) { physical_configurable_child_coordinates_.emplace_back(); logical2physical_configurable_children_.emplace_back(); - logical2physical_configurable_child_instances_.emplace_back(); - logical2physical_configurable_child_parents_.emplace_back(); + logical2physical_configurable_child_instance_names_.emplace_back(); config_region_ids_.emplace_back(); config_region_children_.emplace_back(); @@ -1015,15 +1004,11 @@ void ModuleManager::add_configurable_child(const ModuleId& parent_module, if (type == ModuleManager::e_config_child_type::UNIFIED) { logical2physical_configurable_children_[parent_module].push_back( child_module); - logical2physical_configurable_child_instances_[parent_module].push_back( - child_instance); - logical2physical_configurable_child_parents_[parent_module].push_back( - parent_module); + logical2physical_configurable_child_instance_names_[parent_module].emplace_back(); } else if (type == ModuleManager::e_config_child_type::LOGICAL) { logical2physical_configurable_children_[parent_module].emplace_back(); - logical2physical_configurable_child_instances_[parent_module] + logical2physical_configurable_child_instance_names_[parent_module] .emplace_back(); - logical2physical_configurable_child_parents_[parent_module].emplace_back(); } } @@ -1040,32 +1025,18 @@ void ModuleManager::set_logical2physical_configurable_child( physical_child_module; } -void ModuleManager::set_logical2physical_configurable_child_instance( +void ModuleManager::set_logical2physical_configurable_child_instance_name( const ModuleId& parent_module, const size_t& logical_child_id, - const size_t& physical_child_instance) { + const std::string& physical_child_instance_name) { /* Sanity checks */ VTR_ASSERT(valid_module_id(parent_module)); VTR_ASSERT(logical_child_id < num_configurable_children( parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ - logical2physical_configurable_child_instances_[parent_module] + logical2physical_configurable_child_instance_names_[parent_module] [logical_child_id] = - physical_child_instance; -} - -void ModuleManager::set_logical2physical_configurable_child_parent_module( - const ModuleId& parent_module, const size_t& logical_child_id, - const ModuleId& physical_child_parent_module) { - /* Sanity checks */ - VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(logical_child_id < - num_configurable_children( - parent_module, ModuleManager::e_config_child_type::LOGICAL)); - /* Create the pair */ - logical2physical_configurable_child_parents_[parent_module] - [logical_child_id] = - physical_child_parent_module; + physical_child_instance_name; } void ModuleManager::reserve_configurable_child( @@ -1091,13 +1062,8 @@ void ModuleManager::reserve_configurable_child( num_children); } if (num_children > - logical2physical_configurable_child_instances_[parent_module].size()) { - logical2physical_configurable_child_instances_[parent_module].reserve( - num_children); - } - if (num_children > - logical2physical_configurable_child_parents_[parent_module].size()) { - logical2physical_configurable_child_parents_[parent_module].reserve( + logical2physical_configurable_child_instance_names_[parent_module].size()) { + logical2physical_configurable_child_instance_names_[parent_module].reserve( num_children); } } @@ -1499,8 +1465,7 @@ void ModuleManager::clear_configurable_children(const ModuleId& parent_module) { physical_configurable_child_coordinates_[parent_module].clear(); logical2physical_configurable_children_[parent_module].clear(); - logical2physical_configurable_child_instances_[parent_module].clear(); - logical2physical_configurable_child_parents_[parent_module].clear(); + logical2physical_configurable_child_instance_names_[parent_module].clear(); } void ModuleManager::clear_config_region(const ModuleId& parent_module) { diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index c79ce6b4e..219f790ff 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -192,19 +192,16 @@ class ModuleManager { std::vector> configurable_child_coordinates( const ModuleId& parent_module, const e_config_child_type& type) const; - /* Find all the configurable child modules under a parent module */ - std::vector logical2physical_configurable_children( - const ModuleId& parent_module) const; - /* Find all the instances of configurable child modules under a parent module - */ - std::vector logical2physical_configurable_child_instances( - const ModuleId& parent_module) const; - /* Find all the parent modules of physical configurable child modules under a - * parent module Note that a physical configurable child module may be at + /* Find all the configurable child modules under a parent module + * Note that a physical configurable child module may be at * another module; Only the logical child module is under the current parent * module */ - std::vector logical2physical_configurable_child_parents( + std::vector logical2physical_configurable_children( + const ModuleId& parent_module) const; + /* Find all the instance names of configurable child modules under a parent module + */ + std::vector logical2physical_configurable_child_instance_names( const ModuleId& parent_module) const; /* Find all the I/O child modules under a parent module */ @@ -405,12 +402,9 @@ class ModuleManager { const ModuleId& physical_child_module); /** @brief Create a pair of mapping from a logical configurable child to a * physical configurable child */ - void set_logical2physical_configurable_child_instance( + void set_logical2physical_configurable_child_instance_name( const ModuleId& parent_module, const size_t& logical_child_id, - const size_t& physical_child_instance); - void set_logical2physical_configurable_child_parent_module( - const ModuleId& parent_module, const size_t& logical_child_id, - const ModuleId& physical_child_parent_module); + const std::string& physical_child_instance_name); /* Reserved a number of configurable children for memory efficiency */ void reserve_configurable_child(const ModuleId& module, const size_t& num_children, @@ -584,14 +578,10 @@ class ModuleManager { vtr::vector> logical2physical_configurable_children_; /* Child modules with configurable memory bits that this module contain */ - vtr::vector> - logical2physical_configurable_child_instances_; /* Instances of child + vtr::vector> + logical2physical_configurable_child_instance_names_; /* Instances of child modules with configurable memory bits that this module contain */ - vtr::vector> - logical2physical_configurable_child_parents_; /* Parent modules with - configurable memory bits that this module contain - */ vtr::vector> physical_configurable_children_; /* Child modules with configurable memory diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 4067a663e..bf849c924 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -50,6 +50,7 @@ static std::vector generate_mode_select_bitstream( *******************************************************************/ static void build_primitive_bitstream( BitstreamManager& bitstream_manager, + std::map& grouped_mem_inst_scoreboard, const ConfigBlockId& parent_configurable_block, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& device_annotation, const PhysicalPb& physical_pb, @@ -132,6 +133,21 @@ static void build_primitive_bitstream( mode_select_bitstream.size() == module_manager.module_port(mem_module, mem_out_port_id).get_width()); + /* If there is a feedthrough module, we should consider the scoreboard */ + std::string feedthru_mem_block_name = generate_memory_module_name( + circuit_lib, primitive_model, sram_models[0], std::string(MEMORY_MODULE_POSTFIX), true); + ModuleId feedthru_mem_module = module_manager.find_module(feedthru_mem_block_name); + if (module_manager.valid_module_id(feedthru_mem_module)) { + auto result = grouped_mem_inst_scoreboard.find(mem_block_name); + if (result == grouped_mem_inst_scoreboard.end()) { + /* Update scoreboard */ + grouped_mem_inst_scoreboard[mem_block_name] = 1; + } else { + mem_block_name = generate_instance_name(mem_block_name, result->second); + grouped_mem_inst_scoreboard[mem_block_name]++; + } + } + /* Create a block for the bitstream which corresponds to the memory module * associated to the LUT */ ConfigBlockId mem_block = bitstream_manager.add_block(mem_block_name); @@ -159,6 +175,7 @@ static void build_primitive_bitstream( *******************************************************************/ static void build_physical_block_pin_interc_bitstream( BitstreamManager& bitstream_manager, + std::map& grouped_mem_inst_scoreboard, const ConfigBlockId& parent_configurable_block, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, const AtomContext& atom_ctx, @@ -264,9 +281,6 @@ static void build_physical_block_pin_interc_bitstream( * physical_block */ std::string mem_block_name = generate_pb_memory_instance_name( GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string("")); - ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name); - bitstream_manager.add_child_block(parent_configurable_block, - mux_mem_block); /* Find the module in module manager and ensure the bitstream size * matches! */ @@ -281,6 +295,24 @@ static void build_physical_block_pin_interc_bitstream( module_manager.module_port(mux_mem_module, mux_mem_out_port_id) .get_width()); + /* If there is a feedthrough module, we should consider the scoreboard */ + std::string feedthru_mem_block_name = generate_pb_memory_instance_name( + GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), true); + ModuleId feedthru_mem_module = module_manager.find_module(feedthru_mem_block_name); + if (module_manager.valid_module_id(feedthru_mem_module)) { + auto result = grouped_mem_inst_scoreboard.find(mem_block_name); + if (result == grouped_mem_inst_scoreboard.end()) { + /* Update scoreboard */ + grouped_mem_inst_scoreboard[mem_block_name] = 1; + } else { + mem_block_name = generate_instance_name(mem_block_name, result->second); + grouped_mem_inst_scoreboard[mem_block_name]++; + } + } + ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name); + bitstream_manager.add_child_block(parent_configurable_block, + mux_mem_block); + VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), bitstream_manager.block_name(mux_mem_block).c_str(), @@ -335,6 +367,7 @@ static void build_physical_block_pin_interc_bitstream( *******************************************************************/ static void build_physical_block_interc_port_bitstream( BitstreamManager& bitstream_manager, + std::map& grouped_mem_inst_scoreboard, const ConfigBlockId& parent_configurable_block, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, const AtomContext& atom_ctx, @@ -350,7 +383,7 @@ static void build_physical_block_interc_port_bitstream( for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream( - bitstream_manager, parent_configurable_block, module_manager, + bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->input_pins[iport][ipin]), physical_mode, @@ -364,7 +397,7 @@ static void build_physical_block_interc_port_bitstream( for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream( - bitstream_manager, parent_configurable_block, module_manager, + bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->output_pins[iport][ipin]), physical_mode, @@ -378,7 +411,7 @@ static void build_physical_block_interc_port_bitstream( for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream( - bitstream_manager, parent_configurable_block, module_manager, + bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->clock_pins[iport][ipin]), physical_mode, @@ -398,6 +431,7 @@ static void build_physical_block_interc_port_bitstream( *******************************************************************/ static void build_physical_block_interc_bitstream( BitstreamManager& bitstream_manager, + std::map& grouped_mem_inst_scoreboard, const ConfigBlockId& parent_configurable_block, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, const AtomContext& atom_ctx, @@ -422,7 +456,7 @@ static void build_physical_block_interc_bitstream( * Note: it is not applied to primitive pb_type! */ build_physical_block_interc_port_bitstream( - bitstream_manager, parent_configurable_block, module_manager, circuit_lib, + bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_OUTPUT, physical_mode, verbose); @@ -445,13 +479,13 @@ static void build_physical_block_interc_bitstream( /* For each child_pb_graph_node input pins*/ build_physical_block_interc_port_bitstream( - bitstream_manager, parent_configurable_block, module_manager, + bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_INPUT, physical_mode, verbose); /* For clock pins, we should do the same work */ build_physical_block_interc_port_bitstream( - bitstream_manager, parent_configurable_block, module_manager, + bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_CLOCK, physical_mode, verbose); @@ -464,6 +498,7 @@ static void build_physical_block_interc_bitstream( * This function supports both single-output and fracturable LUTs *******************************************************************/ static void build_lut_bitstream(BitstreamManager& bitstream_manager, + std::map& grouped_mem_inst_scoreboard, const ConfigBlockId& parent_configurable_block, const VprDeviceAnnotation& device_annotation, const ModuleManager& module_manager, @@ -612,6 +647,21 @@ static void build_lut_bitstream(BitstreamManager& bitstream_manager, lut_bitstream.size() == module_manager.module_port(mem_module, mem_out_port_id).get_width()); + /* If there is a feedthrough module, we should consider the scoreboard */ + std::string feedthru_mem_block_name = generate_memory_module_name( + circuit_lib, lut_model, sram_models[0], std::string(MEMORY_MODULE_POSTFIX), true); + ModuleId feedthru_mem_module = module_manager.find_module(feedthru_mem_block_name); + if (module_manager.valid_module_id(feedthru_mem_module)) { + auto result = grouped_mem_inst_scoreboard.find(mem_block_name); + if (result == grouped_mem_inst_scoreboard.end()) { + /* Update scoreboard */ + grouped_mem_inst_scoreboard[mem_block_name] = 1; + } else { + mem_block_name = generate_instance_name(mem_block_name, result->second); + grouped_mem_inst_scoreboard[mem_block_name]++; + } + } + /* Create a block for the bitstream which corresponds to the memory module * associated to the LUT */ ConfigBlockId mem_block = bitstream_manager.add_block(mem_block_name); @@ -638,6 +688,7 @@ static void build_lut_bitstream(BitstreamManager& bitstream_manager, *******************************************************************/ static void rec_build_physical_block_bitstream( BitstreamManager& bitstream_manager, + std::map& grouped_mem_inst_scoreboard, const ConfigBlockId& parent_configurable_block, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, const AtomContext& atom_ctx, @@ -699,7 +750,7 @@ static void rec_build_physical_block_bitstream( } /* Go recursively */ rec_build_physical_block_bitstream( - bitstream_manager, pb_configurable_block, module_manager, circuit_lib, + bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, border_side, physical_pb, child_pb, &(physical_pb_graph_node @@ -719,7 +770,7 @@ static void rec_build_physical_block_bitstream( /* Special case for LUT !!! * Mapped logical block information is stored in child_pbs of this pb!!! */ - build_lut_bitstream(bitstream_manager, pb_configurable_block, + build_lut_bitstream(bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, device_annotation, module_manager, circuit_lib, mux_lib, physical_pb, pb_id, physical_pb_type, verbose); @@ -729,7 +780,7 @@ static void rec_build_physical_block_bitstream( case CIRCUIT_MODEL_IOPAD: /* For other types of blocks, we can apply a generic therapy */ build_primitive_bitstream( - bitstream_manager, pb_configurable_block, module_manager, circuit_lib, + bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, module_manager, circuit_lib, device_annotation, physical_pb, pb_id, physical_pb_type, verbose); break; default: @@ -744,7 +795,7 @@ static void rec_build_physical_block_bitstream( /* Generate the bitstream for the interconnection in this physical block */ build_physical_block_interc_bitstream( - bitstream_manager, pb_configurable_block, module_manager, circuit_lib, + bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, physical_pb_graph_node, physical_pb, physical_mode, verbose); } @@ -838,6 +889,7 @@ static void build_physical_block_bitstream( * If you need different equivalent sites, you can always define * it as a mode under a */ + std::map grouped_mem_inst_scoreboard; for (size_t z = 0; z < place_annotation.grid_blocks(grid_coord).size(); ++z) { int sub_tile_index = device_annotation.physical_tile_z_to_subtile_index(grid_type, z); @@ -854,7 +906,7 @@ static void build_physical_block_bitstream( place_annotation.grid_blocks(grid_coord)[z]) { /* Recursively traverse the pb_graph and generate bitstream */ rec_build_physical_block_bitstream( - bitstream_manager, grid_configurable_block, module_manager, + bitstream_manager, grouped_mem_inst_scoreboard, grid_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, border_side, PhysicalPb(), PhysicalPbId::INVALID(), lb_type->pb_graph_head, z, verbose); @@ -869,7 +921,7 @@ static void build_physical_block_bitstream( /* Recursively traverse the pb_graph and generate bitstream */ rec_build_physical_block_bitstream( - bitstream_manager, grid_configurable_block, module_manager, + bitstream_manager, grouped_mem_inst_scoreboard, grid_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, border_side, phy_pb, top_pb_id, pb_graph_head, z, verbose); diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index 9acba8a1c..a757346ca 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -499,7 +499,9 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( int rec_find_physical_memory_children( const ModuleManager& module_manager, const ModuleId& curr_module, - std::vector& physical_memory_children, const bool& verbose) { + std::vector& physical_memory_children, + std::vector& physical_memory_instance_names, + const bool& verbose) { if (module_manager .configurable_children(curr_module, ModuleManager::e_config_child_type::LOGICAL) @@ -522,59 +524,23 @@ int rec_find_physical_memory_children( physical_memory_children.push_back( module_manager.logical2physical_configurable_children( curr_module)[ichild]); + physical_memory_instance_names.push_back( + module_manager.logical2physical_configurable_instance_names( + curr_module)[ichild]); VTR_LOGV( - verbose, "Collecting physical memory module '%s'...\n", + verbose, "Collecting physical memory module '%s' with an instance name '%s'...\n", module_manager .module_name(module_manager.logical2physical_configurable_children( curr_module)[ichild]) - .c_str()); + .c_str(), + module_manager + .module_name(module_manager.logical2physical_configurable_instance_names( + curr_module)[ichild]) + .c_str(), + ); } else { rec_find_physical_memory_children(module_manager, logical_child, - physical_memory_children, verbose); - } - } - return CMD_EXEC_SUCCESS; -} - -int rec_update_logical_memory_children_with_physical_mapping( - ModuleManager& module_manager, const ModuleId& curr_module, - const ModuleId& phy_mem_module, - std::map& logical_mem_child_inst_count) { - if (module_manager - .configurable_children(curr_module, - ModuleManager::e_config_child_type::LOGICAL) - .empty()) { - return CMD_EXEC_SUCCESS; - } - for (size_t ichild = 0; - ichild < module_manager - .configurable_children( - curr_module, ModuleManager::e_config_child_type::LOGICAL) - .size(); - ++ichild) { - ModuleId logical_child = module_manager.configurable_children( - curr_module, ModuleManager::e_config_child_type::LOGICAL)[ichild]; - if (module_manager - .configurable_children(logical_child, - ModuleManager::e_config_child_type::LOGICAL) - .empty()) { - /* This is a leaf node, update its physical information */ - ModuleId phy_mem_submodule = - module_manager.logical2physical_configurable_children( - curr_module)[ichild]; - auto result = logical_mem_child_inst_count.find(phy_mem_submodule); - if (result == logical_mem_child_inst_count.end()) { - logical_mem_child_inst_count[phy_mem_submodule] = 0; - } - module_manager.set_logical2physical_configurable_child_instance( - curr_module, ichild, logical_mem_child_inst_count[phy_mem_submodule]); - module_manager.set_logical2physical_configurable_child_parent_module( - curr_module, ichild, phy_mem_module); - logical_mem_child_inst_count[phy_mem_submodule]++; - } else { - rec_update_logical_memory_children_with_physical_mapping( - module_manager, logical_child, phy_mem_module, - logical_mem_child_inst_count); + physical_memory_children, physical_memory_instance_names, verbose); } } return CMD_EXEC_SUCCESS; diff --git a/openfpga/src/utils/memory_utils.h b/openfpga/src/utils/memory_utils.h index 3578a8701..e0edff212 100644 --- a/openfpga/src/utils/memory_utils.h +++ b/openfpga/src/utils/memory_utils.h @@ -5,6 +5,7 @@ * Include header files that are required by function declaration *******************************************************************/ #include +#include #include "circuit_types.h" #include "config_protocol.h" @@ -61,20 +62,10 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol( */ int rec_find_physical_memory_children( const ModuleManager& module_manager, const ModuleId& curr_module, - std::vector& physical_memory_children, const bool& verbose); + std::vector& physical_memory_children, + std::vector& physical_memory_instance_names, + const bool& verbose); -/** - * @brief Update all the mappings between logical-to-physical memory children - * with a given root module This function will walk through the module tree in a - * recursive way until reaching the leaf node (which require configurable - * memories) Keep a scoreboard of instance number for checking. Note that when - * calling this the function, use an empty scoreboard! - */ -int rec_update_logical_memory_children_with_physical_mapping( - ModuleManager& module_manager, const ModuleId& curr_module, - const ModuleId& phy_mem_module, - std::map& logical_mem_child_inst_count); - -} /* end namespace openfpga */ + /* end namespace openfpga */ #endif From 22816a7ed486acefd58856942824e547243fd752 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Aug 2023 14:04:57 -0700 Subject: [PATCH 26/41] [core] syntax --- openfpga/src/utils/memory_utils.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index a757346ca..49759403b 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -533,10 +533,8 @@ int rec_find_physical_memory_children( .module_name(module_manager.logical2physical_configurable_children( curr_module)[ichild]) .c_str(), - module_manager - .module_name(module_manager.logical2physical_configurable_instance_names( - curr_module)[ichild]) - .c_str(), + module_manager.logical2physical_configurable_instance_names( + curr_module)[ichild].c_str() ); } else { rec_find_physical_memory_children(module_manager, logical_child, From 2aab94cd6cc5be22eff22d5f5a7a964d8423109a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Aug 2023 14:11:57 -0700 Subject: [PATCH 27/41] [core] syntax --- openfpga/src/utils/memory_utils.cpp | 4 ++-- openfpga/src/utils/memory_utils.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index 49759403b..1efb7dfae 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -525,7 +525,7 @@ int rec_find_physical_memory_children( module_manager.logical2physical_configurable_children( curr_module)[ichild]); physical_memory_instance_names.push_back( - module_manager.logical2physical_configurable_instance_names( + module_manager.logical2physical_configurable_child_instance_names( curr_module)[ichild]); VTR_LOGV( verbose, "Collecting physical memory module '%s' with an instance name '%s'...\n", @@ -533,7 +533,7 @@ int rec_find_physical_memory_children( .module_name(module_manager.logical2physical_configurable_children( curr_module)[ichild]) .c_str(), - module_manager.logical2physical_configurable_instance_names( + module_manager.logical2physical_configurable_child_instance_names( curr_module)[ichild].c_str() ); } else { diff --git a/openfpga/src/utils/memory_utils.h b/openfpga/src/utils/memory_utils.h index e0edff212..7dedb6ddc 100644 --- a/openfpga/src/utils/memory_utils.h +++ b/openfpga/src/utils/memory_utils.h @@ -66,6 +66,6 @@ int rec_find_physical_memory_children( std::vector& physical_memory_instance_names, const bool& verbose); - /* end namespace openfpga */ +} /* end namespace openfpga */ #endif From 68f07d6fc94e714e3785d31e4e1ccf6230fda079 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Aug 2023 20:53:58 -0700 Subject: [PATCH 28/41] [core] code format --- openfpga/src/fabric/build_grid_modules.cpp | 6 +- openfpga/src/fabric/build_memory_modules.cpp | 42 ++--- openfpga/src/fabric/build_memory_modules.h | 16 +- openfpga/src/fabric/build_routing_modules.cpp | 4 +- openfpga/src/fabric/module_manager.cpp | 15 +- openfpga/src/fabric/module_manager.h | 3 +- .../fpga_bitstream/build_grid_bitstream.cpp | 147 +++++++++--------- openfpga/src/utils/memory_utils.cpp | 16 +- openfpga/src/utils/memory_utils.h | 2 +- 9 files changed, 133 insertions(+), 118 deletions(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 118e55f9e..bf8ca86ac 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -695,9 +695,9 @@ static void add_module_pb_graph_pin_interc( mux_mem_module_name.c_str(), phy_mem_module_name.c_str()); module_manager.set_logical2physical_configurable_child( pb_module, config_child_id, phy_mem_module); - std::string phy_mux_mem_instance_name = generate_pb_memory_instance_name( - GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), - false); + std::string phy_mux_mem_instance_name = + generate_pb_memory_instance_name( + GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), false); module_manager.set_logical2physical_configurable_child_instance_name( pb_module, config_child_id, phy_mux_mem_instance_name); VTR_LOGV(verbose, "Now use a feedthrough memory for '%s'\n", diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index da0b1285b..1c6fc02cf 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -7,8 +7,8 @@ #include #include -#include #include +#include #include "build_decoder_modules.h" #include "circuit_library_utils.h" @@ -1304,15 +1304,13 @@ static void add_module_output_nets_to_memory_group_module( * - Add ports * - Add nets ********************************************************************/ -int build_memory_group_module(ModuleManager& module_manager, - DecoderLibrary& decoder_lib, - const CircuitLibrary& circuit_lib, - const e_config_protocol_type& sram_orgz_type, - const std::string& module_name, - const CircuitModelId& sram_model, - const std::vector& child_modules, - const std::vector& child_instance_names, - const size_t& num_mems, const bool& verbose) { +int build_memory_group_module( + ModuleManager& module_manager, DecoderLibrary& decoder_lib, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, const std::string& module_name, + const CircuitModelId& sram_model, const std::vector& child_modules, + const std::vector& child_instance_names, const size_t& num_mems, + const bool& verbose) { VTR_LOGV(verbose, "Building memory group module '%s'...\n", module_name.c_str()); ModuleId mem_module = module_manager.add_module(module_name); @@ -1336,13 +1334,17 @@ int build_memory_group_module(ModuleManager& module_manager, module_manager.add_port(mem_module, outb_port, ModuleManager::MODULE_OUTPUT_PORT); - /* Identify the duplicated instance name: This mainly comes from the grid modules, which contains multi-instanced blocks. Therefore, we just count the duplicated instance names and name each of them with a unique index, e.g., mem_lut -> mem_lut_0, mem_lut_1 etc. The only exception is for the uinque instance name, we keep the original instance name */ + /* Identify the duplicated instance name: This mainly comes from the grid + * modules, which contains multi-instanced blocks. Therefore, we just count + * the duplicated instance names and name each of them with a unique index, + * e.g., mem_lut -> mem_lut_0, mem_lut_1 etc. The only exception is for the + * uinque instance name, we keep the original instance name */ std::vector unique_child_instance_names; unique_child_instance_names.reserve(child_instance_names.size()); std::map unique_child_instance_name_count; for (std::string curr_inst_name : child_instance_names) { unique_child_instance_name_count[curr_inst_name]++; - } + } std::map unique_child_instance_name_scoreboard; for (std::string curr_inst_name : child_instance_names) { if (1 == unique_child_instance_name_count[curr_inst_name]) { @@ -1352,7 +1354,8 @@ int build_memory_group_module(ModuleManager& module_manager, } auto result = unique_child_instance_name_scoreboard.find(curr_inst_name); if (result == unique_child_instance_name_scoreboard.end()) { - unique_child_instance_names.push_back(generate_instance_name(curr_inst_name, result->second)); + unique_child_instance_names.push_back( + generate_instance_name(curr_inst_name, result->second)); unique_child_instance_name_scoreboard[curr_inst_name]++; } } @@ -1366,7 +1369,9 @@ int build_memory_group_module(ModuleManager& module_manager, size_t child_instance = module_manager.num_instance(mem_module, child_module); module_manager.add_child_module(mem_module, child_module, false); - module_manager.set_child_instance_name(mem_module, child_module, child_instance, unique_child_instance_names[ichild]); + module_manager.set_child_instance_name(mem_module, child_module, + child_instance, + unique_child_instance_names[ichild]); module_manager.add_configurable_child( mem_module, child_module, child_instance, ModuleManager::e_config_child_type::UNIFIED); @@ -1501,11 +1506,10 @@ int add_physical_memory_module(ModuleManager& module_manager, module_manager.module_name(curr_module).c_str()); ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name); if (!module_manager.valid_module_id(phy_mem_module)) { - status = build_memory_group_module(module_manager, decoder_lib, circuit_lib, - sram_orgz_type, phy_mem_module_name, - sram_model, required_phy_mem_modules, - required_phy_mem_instance_names, - module_num_config_bits, verbose); + status = build_memory_group_module( + module_manager, decoder_lib, circuit_lib, sram_orgz_type, + phy_mem_module_name, sram_model, required_phy_mem_modules, + required_phy_mem_instance_names, module_num_config_bits, verbose); } if (status != CMD_EXEC_SUCCESS) { VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", diff --git a/openfpga/src/fabric/build_memory_modules.h b/openfpga/src/fabric/build_memory_modules.h index 5a754fa27..b79fd4b90 100644 --- a/openfpga/src/fabric/build_memory_modules.h +++ b/openfpga/src/fabric/build_memory_modules.h @@ -30,15 +30,13 @@ int build_memory_modules(ModuleManager& module_manager, const bool& require_feedthrough_memory, const bool& verbose); -int build_memory_group_module(ModuleManager& module_manager, - DecoderLibrary& decoder_lib, - const CircuitLibrary& circuit_lib, - const e_config_protocol_type& sram_orgz_type, - const std::string& module_name, - const CircuitModelId& sram_model, - const std::vector& child_modules, - const std::vector& child_instance_names, - const size_t& num_mems, const bool& verbose); +int build_memory_group_module( + ModuleManager& module_manager, DecoderLibrary& decoder_lib, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, const std::string& module_name, + const CircuitModelId& sram_model, const std::vector& child_modules, + const std::vector& child_instance_names, const size_t& num_mems, + const bool& verbose); int add_physical_memory_module(ModuleManager& module_manager, DecoderLibrary& decoder_lib, diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp index 174a3e7df..4693db986 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -259,8 +259,8 @@ static void build_switch_block_mux_module( module_manager.set_logical2physical_configurable_child( sb_module, config_child_id, physical_mem_module); std::string physical_mem_instance_name = generate_sb_memory_instance_name( - SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string(""), - false); + SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, + std::string(""), false); module_manager.set_logical2physical_configurable_child_instance_name( sb_module, config_child_id, physical_mem_instance_name); } diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index bc907b100..48d03095d 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -1004,7 +1004,8 @@ void ModuleManager::add_configurable_child(const ModuleId& parent_module, if (type == ModuleManager::e_config_child_type::UNIFIED) { logical2physical_configurable_children_[parent_module].push_back( child_module); - logical2physical_configurable_child_instance_names_[parent_module].emplace_back(); + logical2physical_configurable_child_instance_names_[parent_module] + .emplace_back(); } else if (type == ModuleManager::e_config_child_type::LOGICAL) { logical2physical_configurable_children_[parent_module].emplace_back(); logical2physical_configurable_child_instance_names_[parent_module] @@ -1034,9 +1035,8 @@ void ModuleManager::set_logical2physical_configurable_child_instance_name( num_configurable_children( parent_module, ModuleManager::e_config_child_type::LOGICAL)); /* Create the pair */ - logical2physical_configurable_child_instance_names_[parent_module] - [logical_child_id] = - physical_child_instance_name; + logical2physical_configurable_child_instance_names_ + [parent_module][logical_child_id] = physical_child_instance_name; } void ModuleManager::reserve_configurable_child( @@ -1062,9 +1062,10 @@ void ModuleManager::reserve_configurable_child( num_children); } if (num_children > - logical2physical_configurable_child_instance_names_[parent_module].size()) { - logical2physical_configurable_child_instance_names_[parent_module].reserve( - num_children); + logical2physical_configurable_child_instance_names_[parent_module] + .size()) { + logical2physical_configurable_child_instance_names_[parent_module] + .reserve(num_children); } } if (type == ModuleManager::e_config_child_type::PHYSICAL || diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 219f790ff..235719cbc 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -199,7 +199,8 @@ class ModuleManager { */ std::vector logical2physical_configurable_children( const ModuleId& parent_module) const; - /* Find all the instance names of configurable child modules under a parent module + /* Find all the instance names of configurable child modules under a parent + * module */ std::vector logical2physical_configurable_child_instance_names( const ModuleId& parent_module) const; diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index bf849c924..d8f217b09 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -134,17 +134,19 @@ static void build_primitive_bitstream( module_manager.module_port(mem_module, mem_out_port_id).get_width()); /* If there is a feedthrough module, we should consider the scoreboard */ - std::string feedthru_mem_block_name = generate_memory_module_name( - circuit_lib, primitive_model, sram_models[0], std::string(MEMORY_MODULE_POSTFIX), true); - ModuleId feedthru_mem_module = module_manager.find_module(feedthru_mem_block_name); + std::string feedthru_mem_block_name = + generate_memory_module_name(circuit_lib, primitive_model, sram_models[0], + std::string(MEMORY_MODULE_POSTFIX), true); + ModuleId feedthru_mem_module = + module_manager.find_module(feedthru_mem_block_name); if (module_manager.valid_module_id(feedthru_mem_module)) { auto result = grouped_mem_inst_scoreboard.find(mem_block_name); if (result == grouped_mem_inst_scoreboard.end()) { - /* Update scoreboard */ - grouped_mem_inst_scoreboard[mem_block_name] = 1; + /* Update scoreboard */ + grouped_mem_inst_scoreboard[mem_block_name] = 1; } else { - mem_block_name = generate_instance_name(mem_block_name, result->second); - grouped_mem_inst_scoreboard[mem_block_name]++; + mem_block_name = generate_instance_name(mem_block_name, result->second); + grouped_mem_inst_scoreboard[mem_block_name]++; } } @@ -298,15 +300,17 @@ static void build_physical_block_pin_interc_bitstream( /* If there is a feedthrough module, we should consider the scoreboard */ std::string feedthru_mem_block_name = generate_pb_memory_instance_name( GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), true); - ModuleId feedthru_mem_module = module_manager.find_module(feedthru_mem_block_name); + ModuleId feedthru_mem_module = + module_manager.find_module(feedthru_mem_block_name); if (module_manager.valid_module_id(feedthru_mem_module)) { auto result = grouped_mem_inst_scoreboard.find(mem_block_name); if (result == grouped_mem_inst_scoreboard.end()) { - /* Update scoreboard */ - grouped_mem_inst_scoreboard[mem_block_name] = 1; + /* Update scoreboard */ + grouped_mem_inst_scoreboard[mem_block_name] = 1; } else { - mem_block_name = generate_instance_name(mem_block_name, result->second); - grouped_mem_inst_scoreboard[mem_block_name]++; + mem_block_name = + generate_instance_name(mem_block_name, result->second); + grouped_mem_inst_scoreboard[mem_block_name]++; } } ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name); @@ -383,9 +387,9 @@ static void build_physical_block_interc_port_bitstream( for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, - circuit_lib, mux_lib, atom_ctx, device_annotation, - bitstream_annotation, physical_pb, + bitstream_manager, grouped_mem_inst_scoreboard, + parent_configurable_block, module_manager, circuit_lib, mux_lib, + atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->input_pins[iport][ipin]), physical_mode, verbose); } @@ -397,9 +401,9 @@ static void build_physical_block_interc_port_bitstream( for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, - circuit_lib, mux_lib, atom_ctx, device_annotation, - bitstream_annotation, physical_pb, + bitstream_manager, grouped_mem_inst_scoreboard, + parent_configurable_block, module_manager, circuit_lib, mux_lib, + atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->output_pins[iport][ipin]), physical_mode, verbose); } @@ -411,9 +415,9 @@ static void build_physical_block_interc_port_bitstream( for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, - circuit_lib, mux_lib, atom_ctx, device_annotation, - bitstream_annotation, physical_pb, + bitstream_manager, grouped_mem_inst_scoreboard, + parent_configurable_block, module_manager, circuit_lib, mux_lib, + atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->clock_pins[iport][ipin]), physical_mode, verbose); } @@ -456,10 +460,10 @@ static void build_physical_block_interc_bitstream( * Note: it is not applied to primitive pb_type! */ build_physical_block_interc_port_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, circuit_lib, - mux_lib, atom_ctx, device_annotation, bitstream_annotation, - physical_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_OUTPUT, physical_mode, - verbose); + bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, + module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, + bitstream_annotation, physical_pb_graph_node, physical_pb, + CIRCUIT_PB_PORT_OUTPUT, physical_mode, verbose); /* We check input_pins of child_pb_graph_node and its the input_edges * Iterate over the interconnections between inputs of physical_pb_graph_node @@ -479,16 +483,16 @@ static void build_physical_block_interc_bitstream( /* For each child_pb_graph_node input pins*/ build_physical_block_interc_port_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, - circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, - child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_INPUT, physical_mode, - verbose); + bitstream_manager, grouped_mem_inst_scoreboard, + parent_configurable_block, module_manager, circuit_lib, mux_lib, + atom_ctx, device_annotation, bitstream_annotation, child_pb_graph_node, + physical_pb, CIRCUIT_PB_PORT_INPUT, physical_mode, verbose); /* For clock pins, we should do the same work */ build_physical_block_interc_port_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, parent_configurable_block, module_manager, - circuit_lib, mux_lib, atom_ctx, device_annotation, bitstream_annotation, - child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_CLOCK, physical_mode, - verbose); + bitstream_manager, grouped_mem_inst_scoreboard, + parent_configurable_block, module_manager, circuit_lib, mux_lib, + atom_ctx, device_annotation, bitstream_annotation, child_pb_graph_node, + physical_pb, CIRCUIT_PB_PORT_CLOCK, physical_mode, verbose); } } } @@ -497,16 +501,14 @@ static void build_physical_block_interc_bitstream( * Generate bitstream for a LUT and add it to bitstream manager * This function supports both single-output and fracturable LUTs *******************************************************************/ -static void build_lut_bitstream(BitstreamManager& bitstream_manager, - std::map& grouped_mem_inst_scoreboard, - const ConfigBlockId& parent_configurable_block, - const VprDeviceAnnotation& device_annotation, - const ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const PhysicalPb& physical_pb, - const PhysicalPbId& lut_pb_id, - t_pb_type* lut_pb_type, const bool& verbose) { +static void build_lut_bitstream( + BitstreamManager& bitstream_manager, + std::map& grouped_mem_inst_scoreboard, + const ConfigBlockId& parent_configurable_block, + const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, + const MuxLibrary& mux_lib, const PhysicalPb& physical_pb, + const PhysicalPbId& lut_pb_id, t_pb_type* lut_pb_type, const bool& verbose) { /* Ensure a valid physical pritimive pb */ if (nullptr == lut_pb_type) { VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid lut_pb_type!\n"); @@ -648,17 +650,19 @@ static void build_lut_bitstream(BitstreamManager& bitstream_manager, module_manager.module_port(mem_module, mem_out_port_id).get_width()); /* If there is a feedthrough module, we should consider the scoreboard */ - std::string feedthru_mem_block_name = generate_memory_module_name( - circuit_lib, lut_model, sram_models[0], std::string(MEMORY_MODULE_POSTFIX), true); - ModuleId feedthru_mem_module = module_manager.find_module(feedthru_mem_block_name); + std::string feedthru_mem_block_name = + generate_memory_module_name(circuit_lib, lut_model, sram_models[0], + std::string(MEMORY_MODULE_POSTFIX), true); + ModuleId feedthru_mem_module = + module_manager.find_module(feedthru_mem_block_name); if (module_manager.valid_module_id(feedthru_mem_module)) { auto result = grouped_mem_inst_scoreboard.find(mem_block_name); if (result == grouped_mem_inst_scoreboard.end()) { - /* Update scoreboard */ - grouped_mem_inst_scoreboard[mem_block_name] = 1; + /* Update scoreboard */ + grouped_mem_inst_scoreboard[mem_block_name] = 1; } else { - mem_block_name = generate_instance_name(mem_block_name, result->second); - grouped_mem_inst_scoreboard[mem_block_name]++; + mem_block_name = generate_instance_name(mem_block_name, result->second); + grouped_mem_inst_scoreboard[mem_block_name]++; } } @@ -750,9 +754,9 @@ static void rec_build_physical_block_bitstream( } /* Go recursively */ rec_build_physical_block_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, module_manager, circuit_lib, - mux_lib, atom_ctx, device_annotation, bitstream_annotation, - border_side, physical_pb, child_pb, + bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, + module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, + bitstream_annotation, border_side, physical_pb, child_pb, &(physical_pb_graph_node ->child_pb_graph_nodes[physical_mode->index][ipb][jpb]), jpb, verbose); @@ -770,18 +774,19 @@ static void rec_build_physical_block_bitstream( /* Special case for LUT !!! * Mapped logical block information is stored in child_pbs of this pb!!! */ - build_lut_bitstream(bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, - device_annotation, module_manager, circuit_lib, - mux_lib, physical_pb, pb_id, physical_pb_type, - verbose); + build_lut_bitstream(bitstream_manager, grouped_mem_inst_scoreboard, + pb_configurable_block, device_annotation, + module_manager, circuit_lib, mux_lib, physical_pb, + pb_id, physical_pb_type, verbose); break; case CIRCUIT_MODEL_FF: case CIRCUIT_MODEL_HARDLOGIC: case CIRCUIT_MODEL_IOPAD: /* For other types of blocks, we can apply a generic therapy */ build_primitive_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, module_manager, circuit_lib, - device_annotation, physical_pb, pb_id, physical_pb_type, verbose); + bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, + module_manager, circuit_lib, device_annotation, physical_pb, pb_id, + physical_pb_type, verbose); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, @@ -795,9 +800,10 @@ static void rec_build_physical_block_bitstream( /* Generate the bitstream for the interconnection in this physical block */ build_physical_block_interc_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, module_manager, circuit_lib, - mux_lib, atom_ctx, device_annotation, bitstream_annotation, - physical_pb_graph_node, physical_pb, physical_mode, verbose); + bitstream_manager, grouped_mem_inst_scoreboard, pb_configurable_block, + module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, + bitstream_annotation, physical_pb_graph_node, physical_pb, physical_mode, + verbose); } /******************************************************************** @@ -906,10 +912,11 @@ static void build_physical_block_bitstream( place_annotation.grid_blocks(grid_coord)[z]) { /* Recursively traverse the pb_graph and generate bitstream */ rec_build_physical_block_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, grid_configurable_block, module_manager, - circuit_lib, mux_lib, atom_ctx, device_annotation, - bitstream_annotation, border_side, PhysicalPb(), - PhysicalPbId::INVALID(), lb_type->pb_graph_head, z, verbose); + bitstream_manager, grouped_mem_inst_scoreboard, + grid_configurable_block, module_manager, circuit_lib, mux_lib, + atom_ctx, device_annotation, bitstream_annotation, border_side, + PhysicalPb(), PhysicalPbId::INVALID(), lb_type->pb_graph_head, z, + verbose); } else { const PhysicalPb& phy_pb = cluster_annotation.physical_pb( place_annotation.grid_blocks(grid_coord)[z]); @@ -921,10 +928,10 @@ static void build_physical_block_bitstream( /* Recursively traverse the pb_graph and generate bitstream */ rec_build_physical_block_bitstream( - bitstream_manager, grouped_mem_inst_scoreboard, grid_configurable_block, module_manager, - circuit_lib, mux_lib, atom_ctx, device_annotation, - bitstream_annotation, border_side, phy_pb, top_pb_id, pb_graph_head, - z, verbose); + bitstream_manager, grouped_mem_inst_scoreboard, + grid_configurable_block, module_manager, circuit_lib, mux_lib, + atom_ctx, device_annotation, bitstream_annotation, border_side, + phy_pb, top_pb_id, pb_graph_head, z, verbose); } } } diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index 1efb7dfae..0abf95b07 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -528,17 +528,21 @@ int rec_find_physical_memory_children( module_manager.logical2physical_configurable_child_instance_names( curr_module)[ichild]); VTR_LOGV( - verbose, "Collecting physical memory module '%s' with an instance name '%s'...\n", + verbose, + "Collecting physical memory module '%s' with an instance name " + "'%s'...\n", module_manager .module_name(module_manager.logical2physical_configurable_children( curr_module)[ichild]) .c_str(), - module_manager.logical2physical_configurable_child_instance_names( - curr_module)[ichild].c_str() - ); + module_manager + .logical2physical_configurable_child_instance_names( + curr_module)[ichild] + .c_str()); } else { - rec_find_physical_memory_children(module_manager, logical_child, - physical_memory_children, physical_memory_instance_names, verbose); + rec_find_physical_memory_children( + module_manager, logical_child, physical_memory_children, + physical_memory_instance_names, verbose); } } return CMD_EXEC_SUCCESS; diff --git a/openfpga/src/utils/memory_utils.h b/openfpga/src/utils/memory_utils.h index 7dedb6ddc..184d0be74 100644 --- a/openfpga/src/utils/memory_utils.h +++ b/openfpga/src/utils/memory_utils.h @@ -4,8 +4,8 @@ /******************************************************************** * Include header files that are required by function declaration *******************************************************************/ -#include #include +#include #include "circuit_types.h" #include "config_protocol.h" From a1f8b3c4418ea63cc38dbe09c5bb2f979c73428e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Aug 2023 21:58:03 -0700 Subject: [PATCH 29/41] [core] fixed a bug on bitstream generator on supporting group_config_block --- openfpga/src/fabric/build_memory_modules.cpp | 14 ++++++++++--- .../fpga_bitstream/build_grid_bitstream.cpp | 21 +++++++++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 1c6fc02cf..17d33ccef 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1343,7 +1343,12 @@ int build_memory_group_module( unique_child_instance_names.reserve(child_instance_names.size()); std::map unique_child_instance_name_count; for (std::string curr_inst_name : child_instance_names) { - unique_child_instance_name_count[curr_inst_name]++; + auto result = unique_child_instance_name_count.find(curr_inst_name); + if (result == unique_child_instance_name_count.end()) { + unique_child_instance_name_count[curr_inst_name] = 1; + } else { + unique_child_instance_name_count[curr_inst_name]++; + } } std::map unique_child_instance_name_scoreboard; for (std::string curr_inst_name : child_instance_names) { @@ -1354,9 +1359,12 @@ int build_memory_group_module( } auto result = unique_child_instance_name_scoreboard.find(curr_inst_name); if (result == unique_child_instance_name_scoreboard.end()) { - unique_child_instance_names.push_back( - generate_instance_name(curr_inst_name, result->second)); + unique_child_instance_name_scoreboard[curr_inst_name] = 0; + unique_child_instance_names.push_back(curr_inst_name); + } else { unique_child_instance_name_scoreboard[curr_inst_name]++; + unique_child_instance_names.push_back(generate_instance_name( + curr_inst_name, unique_child_instance_name_scoreboard[curr_inst_name])); } } VTR_ASSERT(unique_child_instance_names.size() == child_instance_names.size()); diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index d8f217b09..80342a20d 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -143,10 +143,11 @@ static void build_primitive_bitstream( auto result = grouped_mem_inst_scoreboard.find(mem_block_name); if (result == grouped_mem_inst_scoreboard.end()) { /* Update scoreboard */ - grouped_mem_inst_scoreboard[mem_block_name] = 1; + grouped_mem_inst_scoreboard[mem_block_name] = 0; } else { - mem_block_name = generate_instance_name(mem_block_name, result->second); grouped_mem_inst_scoreboard[mem_block_name]++; + mem_block_name = generate_instance_name( + mem_block_name, grouped_mem_inst_scoreboard[mem_block_name]); } } @@ -298,19 +299,20 @@ static void build_physical_block_pin_interc_bitstream( .get_width()); /* If there is a feedthrough module, we should consider the scoreboard */ - std::string feedthru_mem_block_name = generate_pb_memory_instance_name( - GRID_MEM_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""), true); + std::string feedthru_mem_block_name = generate_mux_subckt_name( + circuit_lib, mux_model, datapath_mux_size, + std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); ModuleId feedthru_mem_module = module_manager.find_module(feedthru_mem_block_name); if (module_manager.valid_module_id(feedthru_mem_module)) { auto result = grouped_mem_inst_scoreboard.find(mem_block_name); if (result == grouped_mem_inst_scoreboard.end()) { /* Update scoreboard */ - grouped_mem_inst_scoreboard[mem_block_name] = 1; + grouped_mem_inst_scoreboard[mem_block_name] = 0; } else { - mem_block_name = - generate_instance_name(mem_block_name, result->second); grouped_mem_inst_scoreboard[mem_block_name]++; + mem_block_name = generate_instance_name( + mem_block_name, grouped_mem_inst_scoreboard[mem_block_name]); } } ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name); @@ -659,10 +661,11 @@ static void build_lut_bitstream( auto result = grouped_mem_inst_scoreboard.find(mem_block_name); if (result == grouped_mem_inst_scoreboard.end()) { /* Update scoreboard */ - grouped_mem_inst_scoreboard[mem_block_name] = 1; + grouped_mem_inst_scoreboard[mem_block_name] = 0; } else { - mem_block_name = generate_instance_name(mem_block_name, result->second); grouped_mem_inst_scoreboard[mem_block_name]++; + mem_block_name = generate_instance_name( + mem_block_name, grouped_mem_inst_scoreboard[mem_block_name]); } } From beee2369c92ba8883e324198a88f287e1b452e69 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Aug 2023 22:06:17 -0700 Subject: [PATCH 30/41] [core] fixed a bug --- openfpga/src/fpga_verilog/verilog_memory.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_memory.cpp b/openfpga/src/fpga_verilog/verilog_memory.cpp index a257c60a9..365ace6a5 100644 --- a/openfpga/src/fpga_verilog/verilog_memory.cpp +++ b/openfpga/src/fpga_verilog/verilog_memory.cpp @@ -211,9 +211,9 @@ void print_verilog_submodule_memories(const ModuleManager& module_manager, fp << std::endl; /* Create the module name for the memory block */ - std::string feedthru_module_name = generate_memory_module_name( - circuit_lib, model, sram_models[0], - std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX)); + std::string feedthru_module_name = + generate_memory_module_name(circuit_lib, model, sram_models[0], + std::string(MEMORY_MODULE_POSTFIX), true); ModuleId feedthru_mem_module = module_manager.find_module(feedthru_module_name); From 46b1de08c63e26f467e83d0394740815cf13e71c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Aug 2023 22:07:46 -0700 Subject: [PATCH 31/41] [test] fixed a bug --- .../group_config_block_homo_fabric_tile/config/task.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf index 94e540f67..f11512e3e 100644 --- a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf @@ -16,7 +16,7 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_group_tile_config_option=--group_tile ${PATH:TASK_DIR}/config/tile_config.xml From c5b1918e4727c1f6df282091a7e79fbf1738d919 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 13:11:17 -0700 Subject: [PATCH 32/41] [core] fixed a critical bug which causes reg test failures when group_config_block is off --- openfpga/src/utils/module_manager_utils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 7d0db1659..70a62d148 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -1667,8 +1667,9 @@ static void add_module_nets_cmos_memory_frame_decoder_config_bus( } /* Add the decoder as the last configurable children */ - module_manager.add_configurable_child(parent_module, decoder_module, 0, - config_child_type); + module_manager.add_configurable_child( + parent_module, decoder_module, 0, + ModuleManager::e_config_child_type::UNIFIED); } /********************************************************************* From 3e33f262bcdcf9cbedf3cf30e49df2c351ff7061 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 18:59:24 -0700 Subject: [PATCH 33/41] [test] added a new test to validate group_config_block support when fpga_core wrapper is enabled --- ...ock_full_testbench_example_script.openfpga | 2 ++ .../config/task.conf | 1 + .../config/task.conf | 36 +++++++++++++++++++ .../config/tile_config.xml | 1 + .../config/task.conf | 1 + 5 files changed, 41 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/tile_config.xml diff --git a/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga index 244674cd4..5b60dc626 100644 --- a/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga @@ -22,6 +22,8 @@ lut_truth_table_fixup # - Enabled compression on routing architecture modules # - Enable pin duplication on grid modules build_fabric --compress_routing --group_config_block ${OPENFPGA_GROUP_TILE_CONFIG_OPTION} #--verbose +# Add a fpga core between fpga top and the underlying modules +${OPENFPGA_ADD_FPGA_CORE_MODULE} # Write the fabric hierarchy of module graph to a file # This is used by hierarchical PnR flows diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf index f11512e3e..283c7b751 100644 --- a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile/config/task.conf @@ -20,6 +20,7 @@ openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scrip openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_group_tile_config_option=--group_tile ${PATH:TASK_DIR}/config/tile_config.xml +openfpga_add_fpga_core_module= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/task.conf new file mode 100644 index 000000000..81af76bfa --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/task.conf @@ -0,0 +1,36 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Configuration file for running experiments +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs +# Each job execute fpga_flow script on combination of architecture & benchmark +# timeout_each_job is timeout for each job +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +[GENERAL] +run_engine=openfpga_shell +power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml +power_analysis = true +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/group_config_block_full_testbench_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_group_tile_config_option=--group_tile ${PATH:TASK_DIR}/config/tile_config.xml +openfpga_add_fpga_core_module=add_fpga_core_to_fabric --instance_name fpga_core_inst + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = or2 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/tile_config.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper/config/tile_config.xml @@ -0,0 +1 @@ + diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf index f4273fdbf..30732a74d 100644 --- a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf @@ -20,6 +20,7 @@ openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scrip openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_group_tile_config_option= +openfpga_add_fpga_core_module= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml From 26c8b5146cb1114ca6797af5dea1ec90ac7513d8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 21:44:15 -0700 Subject: [PATCH 34/41] [core] fixed a bug where release build will fail --- openfpga/src/fpga_bitstream/build_routing_bitstream.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index 620cafed3..acb83b4a2 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -103,7 +103,8 @@ static void build_switch_block_mux_bitstream( .get_width()); VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), - bitstream_manager.block_name(mux_mem_block).c_str()); + bitstream_manager.block_name(mux_mem_block).c_str(), + bitstream_manager.block_name(bitstream_manager.block_parent(mux_mem_block)).c_str()); /* Add the bistream to the bitstream manager */ bitstream_manager.add_block_bits(mux_mem_block, mux_bitstream); @@ -316,7 +317,8 @@ static void build_connection_block_mux_bitstream( .get_width()); VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), - bitstream_manager.block_name(mux_mem_block).c_str()); + bitstream_manager.block_name(mux_mem_block).c_str(), + bitstream_manager.block_name(bitstream_manager.block_parent(mux_mem_block)).c_str()); /* Add the bistream to the bitstream manager */ bitstream_manager.add_block_bits(mux_mem_block, mux_bitstream); From 0e9cf6e9099bd7bcc33a1b917e7cc56ee39214d9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 22:11:38 -0700 Subject: [PATCH 35/41] [test] added a new testcase to validate heterogeneous fpga using group config block --- ...reconfig_testbench_example_script.openfpga | 78 +++++++++++++++++++ .../config/task.conf | 55 +++++++++++++ .../config/tile_config.xml | 1 + 3 files changed, 134 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/group_config_block_preconfig_testbench_example_script.openfpga create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/tile_config.xml diff --git a/openfpga_flow/openfpga_shell_scripts/group_config_block_preconfig_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/group_config_block_preconfig_testbench_example_script.openfpga new file mode 100644 index 000000000..978feaa56 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/group_config_block_preconfig_testbench_example_script.openfpga @@ -0,0 +1,78 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --device ${OPENFPGA_VPR_DEVICE} --route_chan_width ${OPENFPGA_VPR_ROUTE_CHAN_WIDTH} --clock_modeling ideal ${OPENFPGA_VPR_EXTRA_OPTIONS} + +# Read OpenFPGA architecture definition +read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} + +# Read OpenFPGA simulation settings +read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE} + +# Annotate the OpenFPGA architecture to VPR data base +# to debug use --verbose options +link_openfpga_arch --sort_gsb_chan_node_in_edges + +# Check and correct any naming conflicts in the BLIF netlist +check_netlist_naming_conflict --fix --report ./netlist_renaming.xml + +# Apply fix-up to Look-Up Table truth tables based on packing results +lut_truth_table_fixup + +# Optionally pb pin fixup +${OPENFPGA_PB_PIN_FIXUP_COMMAND} + +# Build the module graph +# - Enabled compression on routing architecture modules +# - Enable pin duplication on grid modules +build_fabric --compress_routing --group_config_block ${OPENFPGA_GROUP_TILE_CONFIG_OPTION} #--verbose +# Add a fpga core between fpga top and the underlying modules +${OPENFPGA_ADD_FPGA_CORE_MODULE} + +# Write the fabric hierarchy of module graph to a file +# This is used by hierarchical PnR flows +write_fabric_hierarchy --file ./fabric_hierarchy.txt + +# Repack the netlist to physical pbs +# This must be done before bitstream generator and testbench generation +# Strongly recommend it is done after all the fix-up have been applied +repack #--verbose + +# Build the bitstream +# - Output the fabric-independent bitstream to a file +build_architecture_bitstream --verbose --write_file fabric_independent_bitstream.xml + +# Build fabric-dependent bitstream +build_fabric_bitstream --verbose + +# Write fabric-dependent bitstream +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text + +# Write the Verilog netlist for FPGA fabric +# - Enable the use of explicit port mapping in Verilog netlist +write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose + +# Write the Verilog testbench for FPGA fabric +# - We suggest the use of same output directory as fabric Verilog netlists +# - Must specify the reference benchmark file if you want to output any testbenches +# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA +# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase +# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts +write_preconfigured_fabric_wrapper --embed_bitstream iverilog --file ./SRC ${OPENFPGA_VERILOG_TESTBENCH_OPTIONS} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} ${OPENFPGA_VERILOG_TESTBENCH_OPTIONS} + +# Write the SDC files for PnR backend +# - Turn on every options here +# FIXME: Not supported yet. +#write_pnr_sdc --file ./SDC + +# Write SDC to disable timing for configure ports +write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc + +# Write the SDC to run timing analysis for a mapped FPGA fabric +write_analysis_sdc --file ./SDC_analysis + +# Finish and exit OpenFPGA +exit + +# Note : +# To run verification at the end of the flow maintain source in ./SRC directory diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/task.conf new file mode 100644 index 000000000..c8f54dda0 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/task.conf @@ -0,0 +1,55 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Configuration file for running experiments +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs +# Each job execute fpga_flow script on combination of architecture & benchmark +# timeout_each_job is timeout for each job +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +[GENERAL] +run_engine=openfpga_shell +power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml +power_analysis = false +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/group_config_block_preconfig_testbench_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_dsp8_caravel_io_skywater130nm_fdhd_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_vpr_extra_options=--constant_net_method route --skip_sync_clustering_and_routing_results on +openfpga_pb_pin_fixup_command = pb_pin_fixup --verbose +openfpga_vpr_device=3x2 +openfpga_vpr_route_chan_width=60 +openfpga_group_tile_config_option=--group_tile ${PATH:TASK_DIR}/config/tile_config.xml +openfpga_verilog_testbench_options= +openfpga_add_fpga_core_module= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_2/mac_2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_4/mac_4.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_6/mac_6.v +bench3=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_8/mac_8.v + +[SYNTHESIS_PARAM] +# Yosys script parameters +bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm_cell_sim.v +bench_yosys_dsp_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm_dsp_map.v +bench_yosys_dsp_map_parameters_common=-D DSP_A_MAXWIDTH=8 -D DSP_B_MAXWIDTH=8 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=mult_8x8 +bench_read_verilog_options_common = -nolatches +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dsp_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys + +bench0_top = mac_2 +bench1_top = mac_4 +bench2_top = mac_6 +bench3_top = mac_8 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/tile_config.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile/config/tile_config.xml @@ -0,0 +1 @@ + From 18acb39fad7595f04d3b82cf8438c97d8b412e22 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 22:12:32 -0700 Subject: [PATCH 36/41] [core] fixed a bug where heterogeneous fabric may fail --- openfpga/src/fabric/build_memory_modules.cpp | 32 ++++++++++--------- .../build_routing_bitstream.cpp | 16 ++++++---- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 17d33ccef..80899a04c 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -1384,26 +1384,28 @@ int build_memory_group_module( mem_module, child_module, child_instance, ModuleManager::e_config_child_type::UNIFIED); /* Wire outputs of child module to outputs of parent module */ - add_module_output_nets_to_memory_group_module( - module_manager, mem_module, out_port_name, child_module, - mem_out_pin_start_index, child_instance); - add_module_output_nets_to_memory_group_module( - module_manager, mem_module, outb_port_name, child_module, - mem_outb_pin_start_index, child_instance); - /* Update pin counter */ ModulePortId child_out_port_id = module_manager.find_module_port(child_module, out_port_name); - mem_out_pin_start_index += - module_manager.module_port(child_module, child_out_port_id).get_width(); - + if (module_manager.valid_module_port_id(child_module, child_out_port_id)) { + add_module_output_nets_to_memory_group_module( + module_manager, mem_module, out_port_name, child_module, + mem_out_pin_start_index, child_instance); + /* Update pin counter */ + mem_out_pin_start_index += + module_manager.module_port(child_module, child_out_port_id).get_width(); + } ModulePortId child_outb_port_id = module_manager.find_module_port(child_module, outb_port_name); - mem_outb_pin_start_index += - module_manager.module_port(child_module, child_outb_port_id).get_width(); + if (module_manager.valid_module_port_id(child_module, child_outb_port_id)) { + add_module_output_nets_to_memory_group_module( + module_manager, mem_module, outb_port_name, child_module, + mem_outb_pin_start_index, child_instance); + /* Update pin counter */ + mem_outb_pin_start_index += + module_manager.module_port(child_module, child_outb_port_id) + .get_width(); + } } - /* Check pin counter */ - VTR_ASSERT(mem_out_pin_start_index == num_mems && - mem_outb_pin_start_index == num_mems); /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index acb83b4a2..28b79e049 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -102,9 +102,11 @@ static void build_switch_block_mux_bitstream( module_manager.module_port(mux_mem_module, mux_mem_out_port_id) .get_width()); - VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), - bitstream_manager.block_name(mux_mem_block).c_str(), - bitstream_manager.block_name(bitstream_manager.block_parent(mux_mem_block)).c_str()); + VTR_LOGV( + verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), + bitstream_manager.block_name(mux_mem_block).c_str(), + bitstream_manager.block_name(bitstream_manager.block_parent(mux_mem_block)) + .c_str()); /* Add the bistream to the bitstream manager */ bitstream_manager.add_block_bits(mux_mem_block, mux_bitstream); @@ -316,9 +318,11 @@ static void build_connection_block_mux_bitstream( module_manager.module_port(mux_mem_module, mux_mem_out_port_id) .get_width()); - VTR_LOGV(verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), - bitstream_manager.block_name(mux_mem_block).c_str(), - bitstream_manager.block_name(bitstream_manager.block_parent(mux_mem_block)).c_str()); + VTR_LOGV( + verbose, "Added %lu bits to '%s' under '%s'\n", mux_bitstream.size(), + bitstream_manager.block_name(mux_mem_block).c_str(), + bitstream_manager.block_name(bitstream_manager.block_parent(mux_mem_block)) + .c_str()); /* Add the bistream to the bitstream manager */ bitstream_manager.add_block_bits(mux_mem_block, mux_bitstream); From 48448be12783ccce531bdee3c9362970c0f42b1a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 23:11:11 -0700 Subject: [PATCH 37/41] [doc] update figures to illustrate --- .../figures/group_config_block_overview.png | Bin 0 -> 76895 bytes .../openfpga_commands/setup_commands.rst | 9 ++++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 docs/source/manual/openfpga_shell/openfpga_commands/figures/group_config_block_overview.png diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/figures/group_config_block_overview.png b/docs/source/manual/openfpga_shell/openfpga_commands/figures/group_config_block_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..44c17a3dc465626c5643cf4bfb7492545fff2d42 GIT binary patch literal 76895 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 P;LF_P(z%j9uiyTEx;i|< literal 0 HcmV?d00001 diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst index 7bb03b27d..3d9a68fcd 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -263,7 +263,14 @@ build_fabric .. option:: --group_config_block - Group configuration memory blocks under each CLB/SB/CB etc. into a centralized configuration memory blocks. When disabled, the configuration memory blocks are placed in a distributed way under CLB/SB/CB etc. For example, each programming resource, e.g., LUT, has a dedicated configuration memory block, being placed in the same module. + Group configuration memory blocks under each CLB/SB/CB etc. into a centralized configuration memory blocks, as depicted in :numref:`fig_group_config_block_overview`. When disabled, the configuration memory blocks are placed in a distributed way under CLB/SB/CB etc. For example, each programming resource, e.g., LUT, has a dedicated configuration memory block, being placed in the same module. + +.. _fig_group_config_block_overview: + +.. figure:: ./figures/group_config_block_overview.png + :width: 100% + + Impact on grouping configuable blocks: before and after .. option:: --duplicate_grid_pin From d579bdba39eaf929d61ca48225c60c0286f24ec0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 23:16:32 -0700 Subject: [PATCH 38/41] [doc] add more explanation on grouped config block --- .../figures/group_config_block_hierarchy.png | Bin 0 -> 227018 bytes .../openfpga_commands/setup_commands.rst | 9 ++++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 docs/source/manual/openfpga_shell/openfpga_commands/figures/group_config_block_hierarchy.png diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/figures/group_config_block_hierarchy.png b/docs/source/manual/openfpga_shell/openfpga_commands/figures/group_config_block_hierarchy.png new file mode 100644 index 0000000000000000000000000000000000000000..e4e3cdcfe1302d15ad97f4cfacca441f739a2688 GIT binary patch literal 227018 zcmeGE2UL?;7e5Lkij4s*G)2nj0Me!Rzzhm1O+iFzR3w1(-U5h>m8MkbC{23r5Kx-Z zA@mlc_ma>;N$$zKGm<#->O0^1e|O!x)@3avJb6yr^|$xg=e)R~rbKo8>~S(OGOBA= zFKdvI9k(GPJ5+h}FnHqO^rQ&$zF*$1CGI*I=ruWZdBMQL@7a4wD@u zyh26}{yX*O=L6vLSu%=0o|BQ?KEU+#v*Ce@`|m$9BRllxJ23E@@bd}$1Fir3eK7vO zevk3wf4zO&CjQ`GpAS_Mnh7-IXoFwJY_96qlaaA}B>X#I)$Rx(BO|9YyLHFmj;e~3 zk+miNy$9C!jrm!pdICMfUt3Z%Bd9gvSEs+5ULN!CdzI z9n~9b3f6YUY-0SE_%EG@9A{%=ld*dMlhU}X_-AwQm+W~{2L~G|0Rd-cXMSg4err1u z0YOPgNr6j30zyK3;0-=|S1X5mE__z@7yi@9U;SJ*wl}gfvvDx9wqhgnd+)xrql4`E z^MrxE{`_a24rZ|Zk*w_hTozcM0O1<}LHq-7EgT8mYgw5&K!jxe zc=_AM`=kA*mAajoF_ZF{)_~S_ zW=7Yn9E|Ni<3C6HYl8p%jX(R65g?F@hp?Hu*v`2mO5wJBt2M#bBOF_pURh_;-kXLr-#cVT$B z2=%x?v+#7o*Vkm;V51{>Oz)y(H9hsF@>LJ6uhUh9aP2Bi38x?$-{s8<&DIFT*(JQ= zVE;TDG4we?UCPTy@1Z?x3YnFwc*T+RK_>$YvPm1-9X=$j>7qJ?Oh7ccHHH~CTD8LY zGgGok-a|R;m8XQ`OID0JF9@%HmM}%p4S_FcS?TVvytV^$pi4#}ii67+EPO$7{AOR#5S1)+_y5yLAb2+}daL zrqw+-BkSby?{TaYC;!Fwi9rnwX*ji9n_>+jCcvl2CqKM6EhIaz8@!fg)DVD)bDK-0 zmFOFFnoCWgJ|pY4rbZ=Z8b4NCwYS(-G-TK0N_DFt=Uz=w#cXm^z{B^5x9HPq5?=y2 zW;(`=+&hz1qa?Ox62?DWK*}|nyF^LY-q-iUP70d$WISb}>wZ8^2}rx~ z+`d&Z<$Zyb9c~wE`{fCPB8N?`mGMFL2>HsAi6G^7%Hj1!7qd&oUNQ>azx;sw0F#8K znQdFF?D&v`(2P{)|Oxw$zBK<4q<)9M+T+WMD_>is?)vaRn*)%5jcQl&X3amid?6S;EeO;Z?O z6QtaByk68TYpjtQ|sQ3F!%S-%)6K|}B%j(XUM4ueA zEH=32>N>UBz^eXhO|EIjJ9L^Gre4XrZtLP5Mphm^Kz!+Ya9UnJ8lV66ua@Q|w0o}RRq|JUyeLskXNIuq|8;I4CBYt1RkjU(9LecR-H{lt6mN5Xr?e2OntMF`!qw{2SX%B+JD*CUk;x#DJxC_U(RsV(Idw=V|f#}ffiM6P| zdiinV_jf=Wx}6z^uOt5tZT??Bj(4F!d|8>4QVwr;4(Gmai5EK5cu*oAI_Ha+(5^R_ zVgCX1e)F!Ok(1Fae5e&wx*pa0anQMG-#l{b#>+gyOs!2qJU$nn+*jTG;r1yl-$XC5 zT6=o@GiThcZE|>c(L(GTGtuCa{!H1y{~c7se?0V%JEN*h`3E$>pL!!`q`14?TQwpX zycmvEIiRrLLVoh-t#HRe=oBTcSbW$4iHnhT=1&h||0ki?r?7X}FE%|oCUu(XvfXmX z6icBvRAC}wmXc_}{*)nY)viszGZkz$M+h7B<=pdUO%2=cgXrCTf;vcx(=9qwY15teR+gfAGhY{D82H*nxMN z#59QSJd{R+_L}k$*Tk7acXXR$q{YRCY-%JXUMXn2_Wuqg$h^DR*dE!7 z#!HddN!_$+_vK0kZGGD-Mef68j=limy-iHtSuBPo1}-9x@#^IL5(`Iv?lTTIcxOM| z8n^TyfaCA8{_Dw|3lwQRpUUkt_oWuB<3@HhmH5yJN_yT;JKW@TBQY&)nc}1)h0}dnM6$0M`Yfjx8=#E&+eEGGZHJubqbA3UjVbh1!QL8eb8nv zl20>=Tj@K!*cPdoorsk7(?rcxG!otG-!%S8s_bh-& zQ;FZkSRYe~gUBNAd&>3Hs^#5lc7Nxc0 z8@Q;%h4Afn0@zSn2DfV@jr@=MF$$h%@!DO5M}Z}Y$Ay`m^b8)jobXB zR*{b5k}G<`Ww~ z>t_1xOn!cWa3a_NS~s4&`oSN>IND;2Ik|?*0885v48G)abTE}mw6KZVH4KPPe&H{h zc}K7VW64ogFKKIaDw4?B(zD9Aiv}L1kB!0IrkW#cL7-F*fPR>O@`o2dy;?jq?k$g0 zj<3|v1k%pjsaVWw;_s;P!jHX!yEVTNv~3aXtxdcZoM*f^P}p?%$;MnaPiON5{2mG) zW9<%1nsK;r8-vKb^L)x)m=fT> z=SfzNwVOqjFtJx#OU_kH!{yFxhcDuQEf4I1)%%?hwCKwjV#n;Xc`VtlAGS&E=eJ)( z3Qd~Cc{r^t^b6xQimQx$RDFRNy*nNMs}FkGV(#*Bp~d#M3coC)cBjO#$mI%5tDwuu zs0E}&NEeYr*0bz|-w||L=(Fes0(d4R&eLse7RkxDKmQhpM~<>Y5^KOXBL4jNMi3mssx#dwGD-# zwGCa3`I(NexZD*h$xBP)*6S&AoVTbR%Ws1)rFxa}iBp<9EFKS#o_d+T<1kFy=Q17R z48uHQaWmbBE>$$++9=S6ABQ(vnjJ$AN!lB$maA85`qR79j-^?uG|S#Xn|iCNmIxm6Y`idzHip{-)y0qJgx;iv;ix>oWFY3m69beu=sF3Fj$9dy46f zIP&Y4Tp9sJb33vQAlb3aD!fM#KZTQ_-DmdKkIyv~U-6|6rA;FHJk_UBZDyt1l#(*q zh34bC={4dyU030X$%e6255rQTGQ@~CAf43=YYkxitS*Y#ofm#0kiAZZjQ zZBe+0JcTxkzzp4%v4>^xE&}h=L-6^CnRMvhL*T(K#0jVaU{sv=Ugl9gSj^`u4~X0$ ziDd)qiByeW3Snz;GGjPTyh`tGzLh|)-3!1-2Uj(Ze(=G}qA#xsjixRUQY$@~dg03#@IlpG5J& zQx`GTujmX+^;lYW7{8NpQ&X?_{4}t`<1}7d3J4mrr>!EwW=49)f8rY%C^XlOZs8Y) z>aQ$Avd8SNk|0O<$zzFhtT=Rd)mqD%%-)i-&g8H+%l<2O+$kDOMl)QuzC7DW7Ql)T z3*CwLaPP54^$z0x6gKn%2NC_Ss5*wKt-8YpKuCRr>THO$Ud3hiWW;!@VYXpfARZdogqowY4u!kHVKM<{FRcid%BC)h} zcRkz7e-q^;7w&(MMkc4DUtF`s44FJRm?A&0-lHcFr~V=tIA_jKQs?~gbjiv`%JDH@ z0m#qD)j{qo45`DpNHbc&=TWK-MO%^x=?yG-T~o>?@QE*dczl<$;Ad7Y1j^)HI| z3x+S1PNR)yqoG?L8PM1<_FAOSY@P6}sk5>heMUDcy0$@7!2ku+wC~35fo7*b`m%Xy zu*h<7)NUd5gJm(Dr+PQSW2et)&=NDPWALJ2VwX~&;+33-!mkN6&uH3;ue~5Jn;na8 z1|Ir4jB~L!8=82xZ>T4vjmKHdos|44-XM22DTUDEzo)(r0_Ba5f^ZC0)y3fPIoq$i zU757eg&csFf<`~q!%)1`c^1G!I6eI&#ks5fVLVXIFpqafyK1{TD4FYoeUxR<=Wqj% zPh4%}$$o&rOt%FRys0fv`YUtL0gV-y0{+zsZfCiz=$h9;>2&Nme{SsDh}(ST)qMS_ zB4($)w;v`^_Lh_HD=77BI}E!mn-rUx8r@Nxj&0f zVz?!9Nia02%E{*+>k=6Z)qhS55)MUbjjs1X18E>zY+fxuek9}g)oG6$vI0o(HPC(2 z^7*WCv=+!rKgVwc;y+Dmp69dm+*|K$Q1?U!yUe_OmM46T1>|}A*=vwOV_Y`EEgTV# z8P!2P+4SN}Zc&j~r%Fhvr7vI6CwAqH)M55;{UDm7s-aXZzw6lDJ^kS2a1X#fZU=?K z`Sj?8Va=bQmOfNspHk+PK~T;~h`8Wdx{q-=HPlVhn(wyC;x=4EqwhQ=Spvy2Fq2l=lH@^8>5r*Nf?dVjH#dnlfq5id-ynnAPySa}7cvn>^Yu)YFb$E*`Q| zuGmF4mBRY>_{$eGf}jQwWAcp-En~*5_Pr{I(tbtF{ss!05fJ9(0!TLW#TL*_adT-H zgY90eKy!wFYY>T6WGV(SJS~cgzY*)1si8N(C49w0LI6zMwmwoHz+8U4=}}vhxPa$Y z4eL~!;Db-E`n>Ra80=WB9BwsGVmjLXK6>JF{~0mOcoP)D!#4_U;WkiU#%>7#Dexe8 zV`9JjGb*Or8VC?@C}kT~-d`Fj1sOK=S^#}s$Iuld-aP#PgowIA`7kSv4iU^Oezy(( zz=UFPsMJ1D{dl#Cuz7N(Nn0$H;oh(_%3pe^}M^2S~fY6-$(+q@uN`uqS5zbskEYdTNCm~4qu7568g z!_v7RkB3onK)G~rJLo0>eC>y*+26anyV<8bwr5k6(Om5Wb@T%}6M~)S$x_Re(GUG+ z1lj=f{sYnyERL4wEUu0L+8igq$5aIp2epLg+$7poF>w^>undGKbgHO7-()(7eP<#p z>~R~Pdt89}6v*mdH0$kZOP`n!B4*V$tZm`Av1O@|Z_xRKh{tU5cO5DHSmhv{8iS>o zxyBs;7)6+e8K7n2u-fM-IATsr}Eb51a`bajJEWj6X8_x5!#6DJG1>x+1#!{3;-nAm@XXos$Zn(_`iquSk zy&GDy(oMqVgY-cdBJbGgQg@XUeXqUs3luQi=n=W+Sj@DE$3U@lT-AVJ!@1xXNnyHQ z8fcG}11mK-Z+{@KO?Fei$RftE=S3WDEhV>(UT

;uHa2P8WU;Um6l*dVcXCF`-Q9 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%0DbQT~m(@U6#xoX^FV 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 B|f>oGGm;51S*g# zNx6cmlx-;eFg*b}q;G+7k+HC57v-|+YXPfRCLbz4j#$rB&l4h?Awv$asIqg zH<8w_=X55u#I<~GgZ1%d|4kr^_6`8LNYmvfDOul&IF~OrS5p5E_N0k77k8g;DCHWI z=9l%~`+uCh2RzpM`#+8-BT+_0qG4obZ%RZWTVy1AE1SZtlZL2_?2*06-XlfXdu5f} z_9&b1zurdUoIdA#fB(mM9FIHWzTdCc>v~=Dd0o#mY?1YzCeK&m;n&j*&7r44H`w51 z5;&EJaomT;cVh#pk0nB@E`&lkxy5Yl7&9+@UUipbRECTR-{BUA*hyNW(W}32 zng$+p-Y&?T$vyd24mSrI4(=@-vD@hyIVZNuX0Z3?pD@EdKm!9a3t{-YW*Ght@b%Tzd()64j zYOq3wC;kI>b`uM7SuTXc`h%&c`}o_Nm!p6M1vxy3H=S=UAQg=pR?`9Zcix%d!p#@G zCj;bstEEz;hGVm1kH&FzDvl6;sC!>d3tq#jOM&~3#y9MPVM5p{GVVmFDlTTn{&+HD zLE9zt=EC{UTY2B?VRGbn5KR#G&T}v!r5C&dC#=P8%OS`P$iUxyEeCZ4C$`ell(8`XXW$D*62e;!a77XB>w=8 z2dQtK!CL@x@|0qvyl{doqqj(I_e}snaeKJ>0o3R9m2_7ZmaH!aBWLmiJE9{Oaig4N z1f>(MTm!+d8O0vYXib3=$qt?PaZ{-7i`7>=cFNZ4o8{q_f%M9R{rT&El8WZ@xNi;G zhx7F6L)3S?0~h*csMH`i*GYlao#`P7qNBP%;~(32>JlWYFDPj zTuvj<3U-U$oaW+ulImRxco5rgENqlqKf4;&BsC{HCz#al1WU%Z2pkiV2tUi0mcH>) zN4NA^rkjxN^MjYwGj3SC~;tD1+|&^_#gfJc8AhoXiq%hWobjBBFG9xGGrI^0-Cc)`@;iYP0 ze44h6LeBbPh0bT|h36}!b@FpD__+10$9$$WaQ5lq{^8-pqB?N9OICpsGPifJkR7}C zH0IU-loq`le9k*4W0iE-wF@?tgkE%CE{bm+?tV#!&G-Vn zsaf`?QChZKOQlg~#iy=}ClsG&I}flqCE0}vQ2So@Q6cutbh4?6l{<_@%phA4K~-J` z*I%se@SJJK_kr9KR_TyBAb}#p7SMUXq^PuYdFQ=L`Ku{I*^AK_4om^;<2uYi>#ckm zgoPdQtO8nl$e}DORo#mguMa-e%#7?bd1_cr_>rN~^zeahdyzB=uPO2RH^*roSCd%p zk4+)?roLagrJi4M#DMnl(GNWWERE!2T)3I|onh)SCV# zv0@Jam4pV(Ay|Jmm;swY{3h@-AL2BuH?Dtm0-73>F%i)iqGSInnZCkBt7Gu=eq7Yc zvvaAehT*JEg&h`WjnFy!WzMTA=w7^F-zLHe0^yMI{c-!L`%NdQzU6U_I}<;O1~c7IhI@^4UqMTbVf(SX&kzq1+|6lw6aV@q3{eUl$Tb`23bucR zOoaH`^ZxGt?HvSc%OpR0jzF?hd>;G1 zRCCJk$iZup=9Ot|=9dULlWXf3#uGBo-Fg)5yd&oT!T@JB_q|R38u26Q6O(O$B7eF7 ze_S#1sZ4nZwgA_sIS+C|Y@ge{TJZF*-k*H;a*_5O`$n#mgk+W@!**mkTcnQ{^=@GE zAKhLHs*~

a~>1OXC;wDkzi+oU-CUke$@Mkg9M3OL!!FA> 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

Dl5&6`(ubp#`iutF5?(JUu1%NbLs&KQ6w?)CyA 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 literal 0 HcmV?d00001 diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst index 3d9a68fcd..9f6feacce 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -263,7 +263,7 @@ build_fabric .. option:: --group_config_block - Group configuration memory blocks under each CLB/SB/CB etc. into a centralized configuration memory blocks, as depicted in :numref:`fig_group_config_block_overview`. When disabled, the configuration memory blocks are placed in a distributed way under CLB/SB/CB etc. For example, each programming resource, e.g., LUT, has a dedicated configuration memory block, being placed in the same module. + Group configuration memory blocks under each CLB/SB/CB etc. into a centralized configuration memory blocks, as depicted in :numref:`fig_group_config_block_overview`. When disabled, the configuration memory blocks are placed in a distributed way under CLB/SB/CB etc. For example, each programming resource, e.g., LUT, has a dedicated configuration memory block, being placed in the same module. When enabled, as illustrated in :numref:`fig_group_config_block_hierarchy`, the physical memory block locates under a CLB, driving a number of logical memory blocks which are close to the programmable resources. The logical memory blocks contain only pass-through wires which can be optimized out during physical design phase. .. _fig_group_config_block_overview: @@ -271,6 +271,13 @@ build_fabric :width: 100% Impact on grouping configuable blocks: before and after + +.. _fig_group_config_block_hierarchy: + +.. figure:: ./figures/group_config_block_hierarchy.png + :width: 100% + + Netlist hierarchy on grouped configuable blocks .. option:: --duplicate_grid_pin From d402d2322d5a26a91a6ca370d23295a8b3d23e70 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 23:24:59 -0700 Subject: [PATCH 39/41] [doc] syntax --- .../openfpga_commands/setup_commands.rst | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst index 9f6feacce..62ec61e21 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -265,20 +265,21 @@ build_fabric Group configuration memory blocks under each CLB/SB/CB etc. into a centralized configuration memory blocks, as depicted in :numref:`fig_group_config_block_overview`. When disabled, the configuration memory blocks are placed in a distributed way under CLB/SB/CB etc. For example, each programming resource, e.g., LUT, has a dedicated configuration memory block, being placed in the same module. When enabled, as illustrated in :numref:`fig_group_config_block_hierarchy`, the physical memory block locates under a CLB, driving a number of logical memory blocks which are close to the programmable resources. The logical memory blocks contain only pass-through wires which can be optimized out during physical design phase. -.. _fig_group_config_block_overview: - -.. figure:: ./figures/group_config_block_overview.png - :width: 100% - - Impact on grouping configuable blocks: before and after - -.. _fig_group_config_block_hierarchy: - -.. figure:: ./figures/group_config_block_hierarchy.png - :width: 100% - - Netlist hierarchy on grouped configuable blocks + .. _fig_group_config_block_overview: + .. figure:: ./figures/group_config_block_overview.png + :width: 100% + + Impact on grouping configuable blocks: before and after + + .. _fig_group_config_block_hierarchy: + + .. figure:: ./figures/group_config_block_hierarchy.png + :width: 100% + + Netlist hierarchy on grouped configuable blocks + + .. option:: --duplicate_grid_pin Enable pin duplication on grid modules. This is optional unless ultra-dense layout generation is needed From 4ed83cdb17a5ac184f74e240de98b20bc3a57063 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 23:26:14 -0700 Subject: [PATCH 40/41] [doc] add missing file --- .../manual/file_formats/tile_config_file.rst | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/source/manual/file_formats/tile_config_file.rst diff --git a/docs/source/manual/file_formats/tile_config_file.rst b/docs/source/manual/file_formats/tile_config_file.rst new file mode 100644 index 000000000..c3dfcbbce --- /dev/null +++ b/docs/source/manual/file_formats/tile_config_file.rst @@ -0,0 +1,39 @@ +.. _file_formats_tile_config_file: + +Tile Organization (.xml) +------------------------ + +The XML-based description language is used to describe how each tile is composed. +For example, what programmable blocks, connection blocks and switch blocks should be included. + +Using the description language, users can customize the tiles of an FPGA fabric, as detailed as each component in each tile. + +Under the root node ````, the detailes of tile organization can be described. + +.. code-block:: xml + + + + +Syntax +`````` + +Detailed syntax are presented as follows. + +.. option:: style="" + + Specify the style of tile organization. Can be [``top_left`` | ``top_right`` | ``bottom_left`` | ``bottom_right`` | ``custom``] + + .. warning:: Currently, only ``top_left`` is supported! + + The ``top_left`` is a shortcut to define the organization for all the tiles. :numref:`fig_tile_style_top_left` shows an example of tiles in the top-left sytle, where the programmable block locates in the top-left corner of all the tiles, surrounded by two connection blocks and one switch blocks. + +.. _fig_tile_style_top_left: + +.. figure:: ./figures/tile_style_top_left.png + :width: 100% + :alt: An example of top-left style of tile + + An example of top-left style of a tile in FPGA fabric + + From 4d37421735abeed43d07222ef0578c44f2e8b508 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Aug 2023 10:40:22 -0700 Subject: [PATCH 41/41] [core] fixed a bug on loading subkey to support fabric keys --- openfpga/src/fabric/build_device_module.cpp | 2 +- openfpga/src/fabric/build_top_module.cpp | 8 +++++--- openfpga/src/fabric/build_top_module.h | 3 ++- .../build_top_module_child_fine_grained_instance.cpp | 5 +++-- .../build_top_module_child_fine_grained_instance.h | 2 +- .../fabric/build_top_module_child_tile_instance.cpp | 6 ++++-- .../fabric/build_top_module_child_tile_instance.h | 3 ++- openfpga/src/utils/module_manager_memory_utils.cpp | 12 ++++++++---- openfpga/src/utils/module_manager_memory_utils.h | 3 ++- 9 files changed, 28 insertions(+), 16 deletions(-) diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index e4ac8d75b..e20413f3e 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -134,7 +134,7 @@ int build_device_module_graph( openfpga_ctx.device_rr_gsb(), openfpga_ctx.tile_direct(), openfpga_ctx.arch().arch_direct, openfpga_ctx.arch().config_protocol, sram_model, fabric_tile, frame_view, compress_routing, duplicate_grid_pin, - fabric_key, generate_random_fabric_key, verbose); + fabric_key, generate_random_fabric_key, group_config_block, verbose); if (CMD_EXEC_FATAL_ERROR == status) { return status; diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index ce133957a..e8e5066a1 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -57,7 +57,8 @@ int build_top_module( const CircuitModelId& sram_model, const FabricTile& fabric_tile, const bool& frame_view, const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, const FabricKey& fabric_key, - const bool& generate_random_fabric_key, const bool& verbose) { + const bool& generate_random_fabric_key, const bool& group_config_block, + const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build FPGA fabric module"); int status = CMD_EXEC_SUCCESS; @@ -75,14 +76,15 @@ int build_top_module( module_manager, top_module, blwl_sr_banks, circuit_lib, clk_ntwk, rr_clock_lookup, vpr_device_annotation, grids, tile_annotation, rr_graph, device_rr_gsb, tile_direct, arch_direct, config_protocol, sram_model, - frame_view, compact_routing_hierarchy, duplicate_grid_pin, fabric_key); + frame_view, compact_routing_hierarchy, duplicate_grid_pin, fabric_key, + group_config_block); } else { /* TODO: Build the tile instances under the top module */ status = build_top_module_tile_child_instances( module_manager, top_module, blwl_sr_banks, circuit_lib, clk_ntwk, rr_clock_lookup, vpr_device_annotation, grids, tile_annotation, rr_graph, device_rr_gsb, tile_direct, arch_direct, fabric_tile, config_protocol, - sram_model, fabric_key, frame_view, verbose); + sram_model, fabric_key, group_config_block, frame_view, verbose); } if (status != CMD_EXEC_SUCCESS) { diff --git a/openfpga/src/fabric/build_top_module.h b/openfpga/src/fabric/build_top_module.h index 46fe36219..d62ac4993 100644 --- a/openfpga/src/fabric/build_top_module.h +++ b/openfpga/src/fabric/build_top_module.h @@ -44,7 +44,8 @@ int build_top_module( const CircuitModelId& sram_model, const FabricTile& fabric_tile, const bool& frame_view, const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, const FabricKey& fabric_key, - const bool& generate_random_fabric_key, const bool& verbose); + const bool& generate_random_fabric_key, const bool& group_config_block, + const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_child_fine_grained_instance.cpp b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.cpp index 109f29c08..78e2565d0 100644 --- a/openfpga/src/fabric/build_top_module_child_fine_grained_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.cpp @@ -436,7 +436,7 @@ int build_top_module_fine_grained_child_instances( const ArchDirect& arch_direct, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const bool& frame_view, const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, - const FabricKey& fabric_key) { + const FabricKey& fabric_key, const bool& group_config_block) { int status = CMD_EXEC_SUCCESS; std::map> cb_instance_ids; @@ -529,7 +529,8 @@ int build_top_module_fine_grained_child_instances( /* Update the memory organization in sub module (non-top) */ status = load_submodules_memory_modules_from_fabric_key( - module_manager, circuit_lib, config_protocol, fabric_key); + module_manager, circuit_lib, config_protocol, fabric_key, + group_config_block); if (CMD_EXEC_FATAL_ERROR == status) { return status; } diff --git a/openfpga/src/fabric/build_top_module_child_fine_grained_instance.h b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.h index 7db61cb93..41628ac79 100644 --- a/openfpga/src/fabric/build_top_module_child_fine_grained_instance.h +++ b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.h @@ -43,7 +43,7 @@ int build_top_module_fine_grained_child_instances( const ArchDirect& arch_direct, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const bool& frame_view, const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, - const FabricKey& fabric_key); + const FabricKey& fabric_key, const bool& group_config_block); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp index 97c432899..664be1ceb 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1864,7 +1864,8 @@ int build_top_module_tile_child_instances( const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, const ArchDirect& arch_direct, const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, - const FabricKey& fabric_key, const bool& frame_view, const bool& verbose) { + const FabricKey& fabric_key, const bool& group_config_block, + const bool& frame_view, const bool& verbose) { int status = CMD_EXEC_SUCCESS; vtr::Matrix tile_instance_ids; status = add_top_module_tile_instances(module_manager, top_module, @@ -1946,7 +1947,8 @@ int build_top_module_tile_child_instances( /* Update the memory organization in sub module (non-top) */ status = load_submodules_memory_modules_from_fabric_key( - module_manager, circuit_lib, config_protocol, fabric_key); + module_manager, circuit_lib, config_protocol, fabric_key, + group_config_block); if (CMD_EXEC_FATAL_ERROR == status) { return status; } diff --git a/openfpga/src/fabric/build_top_module_child_tile_instance.h b/openfpga/src/fabric/build_top_module_child_tile_instance.h index 02df43e76..5adc39c75 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -42,7 +42,8 @@ int build_top_module_tile_child_instances( const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, const ArchDirect& arch_direct, const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, - const FabricKey& fabric_key, const bool& frame_view, const bool& verbose); + const FabricKey& fabric_key, const bool& group_config_block, + const bool& frame_view, const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/utils/module_manager_memory_utils.cpp b/openfpga/src/utils/module_manager_memory_utils.cpp index b7170f833..cf033e9ed 100644 --- a/openfpga/src/utils/module_manager_memory_utils.cpp +++ b/openfpga/src/utils/module_manager_memory_utils.cpp @@ -516,7 +516,8 @@ static int rebuild_submodule_configurable_children_nets( static int load_and_update_submodule_memory_modules_from_fabric_key( ModuleManager& module_manager, const ModuleId& module_id, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, - const FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { + const FabricKey& fabric_key, const FabricKeyModuleId& key_module_id, + const bool& group_config_block) { int status = CMD_EXEC_SUCCESS; /* Compare the configurable children list */ if (submodule_memory_modules_match_fabric_key(module_manager, module_id, @@ -533,7 +534,9 @@ static int load_and_update_submodule_memory_modules_from_fabric_key( /* Overwrite the configurable children list */ status = update_submodule_memory_modules_from_fabric_key( module_manager, module_id, circuit_lib, config_protocol, - ModuleManager::e_config_child_type::PHYSICAL, fabric_key, key_module_id); + group_config_block ? ModuleManager::e_config_child_type::PHYSICAL + : ModuleManager::e_config_child_type::UNIFIED, + fabric_key, key_module_id); if (status == CMD_EXEC_FATAL_ERROR) { return status; } @@ -553,7 +556,8 @@ static int load_and_update_submodule_memory_modules_from_fabric_key( *******************************************************************/ int load_submodules_memory_modules_from_fabric_key( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const ConfigProtocol& config_protocol, const FabricKey& fabric_key) { + const ConfigProtocol& config_protocol, const FabricKey& fabric_key, + const bool& group_config_block) { int status = CMD_EXEC_SUCCESS; for (FabricKeyModuleId key_module_id : fabric_key.modules()) { std::string module_name = fabric_key.module_name(key_module_id); @@ -569,7 +573,7 @@ int load_submodules_memory_modules_from_fabric_key( /* This is a valid module, try to load and update */ status = load_and_update_submodule_memory_modules_from_fabric_key( module_manager, module_id, circuit_lib, config_protocol, fabric_key, - key_module_id); + key_module_id, group_config_block); if (status == CMD_EXEC_FATAL_ERROR) { return status; } diff --git a/openfpga/src/utils/module_manager_memory_utils.h b/openfpga/src/utils/module_manager_memory_utils.h index a0573bc98..5f7a75b8e 100644 --- a/openfpga/src/utils/module_manager_memory_utils.h +++ b/openfpga/src/utils/module_manager_memory_utils.h @@ -34,7 +34,8 @@ namespace openfpga { int load_submodules_memory_modules_from_fabric_key( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const ConfigProtocol& config_protocol, const FabricKey& fabric_key); + const ConfigProtocol& config_protocol, const FabricKey& fabric_key, + const bool& group_config_block); } /* end namespace openfpga */